Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

137 lines
5.3 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
.. Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
.. http://www.apache.org/licenses/LICENSE-2.0
.. Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
reduction → shared
==================
The ``shared`` variant lowers a reduction (``sum`` / ``max`` / ``min``) when
**both source and destination are shared** memory. At CTA / warpgroup / warp scope
it partitions the threads into groups — one group per output position — has each
thread gather a chunk of the reduction axis, then folds the group with an adaptive
``__shfl_xor`` tree. Source:
``python/tvm/backend/cuda/operator/tile_primitive/reduction/shared.py``.
What it accepts
---------------
.. code-block:: python
@register_dispatch(op_name, "cuda", variant="shared", priority=10, when=[
predicate("storage_scope", _match_reduction_storage_scope, expected_scope=["shared*"]),
predicate("shared_valid", validate_reduction_shared),
])
.. list-table::
:header-rows: 1
:widths: 22 78
* - Property
- Requirement
* - target / priority
- ``cuda``; priority ``10``
* - operand scope
- src **and** dst in ``shared*``, equal dtype
* - exec scope
- ``cta`` / ``warpgroup`` / ``warp`` (shuffle tree) or ``thread`` (sequential)
* - thread binding
- ``threadIdx.x`` present and **1-D** (no ``threadIdx.y`` / ``z``)
* - shape
- ``dst`` size equals the source's spatial extent (product of the non-reduced
dims)
Demonstration program
----------------------
A 32-thread CTA reduces each row of a ``4×8`` ``float32`` shared tile (reduce
axis ``-1``) to a ``4``-vector (from ``test_reduction.py``):
.. code-block:: python
@T.prim_func
def test_reduction(A_ptr: T.handle, B_ptr: T.handle):
A = T.match_buffer(A_ptr, (4, 8), "float32", layout=TileLayout(S[(4, 8)]))
B = T.match_buffer(B_ptr, (4,), "float32", layout=TileLayout(S[(4,)]))
T.device_entry(); T.cta_id([1]); T.thread_id([32])
A_smem = T.alloc_buffer((4, 8), "float32", scope="shared", layout=TileLayout(S[(4, 8)]))
B_smem = T.alloc_buffer((4,), "float32", scope="shared", layout=TileLayout(S[(4,)]))
Tx.cta.copy(A_smem, A); T.cuda.cta_sync()
Tx.cta.sum(B_smem, A_smem, axes=(-1,), accum=False) # reduction shared dispatch
T.cuda.cta_sync()
Tx.cta.copy(B, B_smem)
Algorithm
---------
**1. Choose the group size.** ``group_size = min(next_power_of_2(reduction_len),
32, thread_cnt)`` — here ``reduction_len = 8````group_size = 8``. Each group of 8
lanes cooperatively reduces one row; the CTA processes the 4 rows in parallel.
**2. Gather + shuffle tree.** Each lane loads its slice of the reduction axis into a
register, then ``log2(group_size)`` ``shfl_xor`` steps (masks ``1, 2, 4``) fold the
group; lane 0 of each group writes the result, followed by a barrier:
.. code-block:: python
mask = T.tvm_warp_activemask()
for i in range(n_shuffles): # n_shuffles = log2(group_size)
thread_data[0] = op(thread_data[0],
T.tvm_warp_shuffle_xor(mask, thread_data[0], 1 << i, group_size, 32))
(``warp`` uses ``warp_sync``; ``warpgroup`` ``warpgroup_sync(8)``; ``cta``
``cta_sync``. Thread scope is instead the sequential loop of :doc:`local`.)
Generated TIRx IR
-----------------
.. code-block:: python
thread_data[0] = thread_data[0] + T.tvm_warp_shuffle_xor(T.tvm_warp_activemask(), thread_data[0], 1, 8, 32)
thread_data[0] = thread_data[0] + T.tvm_warp_shuffle_xor(T.tvm_warp_activemask(), thread_data[0], 2, 8, 32)
thread_data[0] = thread_data[0] + T.tvm_warp_shuffle_xor(T.tvm_warp_activemask(), thread_data[0], 4, 8, 32)
Generated CUDA
--------------
.. code-block:: c++
thread_data_ptr[0] = thread_data_ptr[0] + __shfl_xor_sync(__activemask(), thread_data_ptr[0], 1, 8);
thread_data_ptr[0] = thread_data_ptr[0] + __shfl_xor_sync(__activemask(), thread_data_ptr[0], 2, 8);
thread_data_ptr[0] = thread_data_ptr[0] + __shfl_xor_sync(__activemask(), thread_data_ptr[0], 4, 8);
(Verified on ``sm_100a`` — each ``B[r] == sum(A[r, :])``. The shuffle width ``8``
is the group size, not the full warp.)
How inputs change the algorithm
-------------------------------
.. list-table::
:header-rows: 1
:widths: 28 72
* - input
- effect
* - op
- ``sum````+`` shuffle tree; ``max`` / ``min`` → the corresponding combine
* - reduction length / thread count
- set ``group_size = min(next_pow2(reduction_len), 32, thread_cnt)`` and hence
the number of shuffle steps
* - exec scope
- ``cta`` / ``warpgroup`` / ``warp`` → shuffle tree (different sync); ``thread``
→ sequential loop
* - accum
- ``True`` combines the reduced value with the old dst