nbxtensor 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nbxtensor/THIRD_PARTY_NOTICES.md +287 -0
- nbxtensor/__init__.py +17 -0
- nbxtensor/backend/__init__.py +1 -0
- nbxtensor/backend/capabilities.py +207 -0
- nbxtensor/backend/cpu/__init__.py +1 -0
- nbxtensor/backend/cpu/blas.py +102 -0
- nbxtensor/backend/cpu/pool.py +204 -0
- nbxtensor/backend/cpu_backend.py +162 -0
- nbxtensor/backend/device_facts.py +105 -0
- nbxtensor/backend/device_files.py +129 -0
- nbxtensor/backend/device_names.py +18 -0
- nbxtensor/backend/devices/amd/cdna.yml +228 -0
- nbxtensor/backend/devices/amd/cdna2.yml +228 -0
- nbxtensor/backend/devices/amd/cdna3.yml +227 -0
- nbxtensor/backend/devices/apple/apple_m1.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_pro.yml +308 -0
- nbxtensor/backend/devices/apple/apple_m5.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_max.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_pro.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_ultra.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m6.yml +269 -0
- nbxtensor/backend/devices/apple/apple_silicon.yml +263 -0
- nbxtensor/backend/devices/nvidia/ampere.yml +214 -0
- nbxtensor/backend/devices/nvidia/hopper.yml +213 -0
- nbxtensor/backend/devices/nvidia/volta.yml +411 -0
- nbxtensor/backend/faults.py +151 -0
- nbxtensor/backend/host_memory.py +185 -0
- nbxtensor/backend/launcher.py +2350 -0
- nbxtensor/backend/launcher_contract.py +429 -0
- nbxtensor/backend/metal_backend.py +661 -0
- nbxtensor/backend/metal_device.py +1274 -0
- nbxtensor/backend/process_memory.py +73 -0
- nbxtensor/backend/replay_graph.py +322 -0
- nbxtensor/backend/runtime.py +409 -0
- nbxtensor/backend/triton_ext_driver.py +735 -0
- nbxtensor/dtype/__init__.py +1 -0
- nbxtensor/dtype/convert.py +107 -0
- nbxtensor/dtype/dtypes.yml +70 -0
- nbxtensor/dtype/itemsize.py +166 -0
- nbxtensor/dtype/nbx_dtype.py +72 -0
- nbxtensor/dtype/operands.py +158 -0
- nbxtensor/dtype/outputs.py +64 -0
- nbxtensor/dtype/policy.py +155 -0
- nbxtensor/dtype/policy.yml +51 -0
- nbxtensor/dtype/promotion.py +112 -0
- nbxtensor/host.py +55 -0
- nbxtensor/io/__init__.py +1 -0
- nbxtensor/io/read_rate.py +197 -0
- nbxtensor/io/safetensors.py +314 -0
- nbxtensor/memory/__init__.py +9 -0
- nbxtensor/memory/allocator.py +1988 -0
- nbxtensor/memory/arena.py +75 -0
- nbxtensor/memory/device_memory.py +150 -0
- nbxtensor/memory/device_transfer.py +154 -0
- nbxtensor/memory/layout.py +43 -0
- nbxtensor/memory/memory_pool.py +191 -0
- nbxtensor/memory/metal.py +96 -0
- nbxtensor/memory/oom_advice.py +91 -0
- nbxtensor/memory/pointer_tables.py +323 -0
- nbxtensor/memory/pool.py +507 -0
- nbxtensor/memory/shadow.py +43 -0
- nbxtensor/memory/slab.py +273 -0
- nbxtensor/memory/trace_replay.py +122 -0
- nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +13610 -0
- nbxtensor/memory/vmm.py +118 -0
- nbxtensor/ops/__init__.py +1 -0
- nbxtensor/ops/_autotune_policy.py +90 -0
- nbxtensor/ops/_common.py +32 -0
- nbxtensor/ops/_configs.py +1041 -0
- nbxtensor/ops/_cost.py +231 -0
- nbxtensor/ops/abs.py +17 -0
- nbxtensor/ops/adaptive_avg_pool2d.py +161 -0
- nbxtensor/ops/add.py +96 -0
- nbxtensor/ops/addcdiv.py +29 -0
- nbxtensor/ops/addcmul.py +29 -0
- nbxtensor/ops/addmv_op.py +64 -0
- nbxtensor/ops/addr.py +68 -0
- nbxtensor/ops/all_reduce.py +83 -0
- nbxtensor/ops/amax.py +40 -0
- nbxtensor/ops/any_reduce.py +83 -0
- nbxtensor/ops/arange_op.py +27 -0
- nbxtensor/ops/argmax.py +107 -0
- nbxtensor/ops/argmin.py +93 -0
- nbxtensor/ops/atan2.py +71 -0
- nbxtensor/ops/attention_wrappers.py +1648 -0
- nbxtensor/ops/autotune_bucket.py +137 -0
- nbxtensor/ops/autotune_cache.py +430 -0
- nbxtensor/ops/autotune_certified.py +1262 -0
- nbxtensor/ops/autotune_refusals.py +380 -0
- nbxtensor/ops/autotune_settings.py +58 -0
- nbxtensor/ops/avg_pool2d.py +73 -0
- nbxtensor/ops/baddbmm_op.py +132 -0
- nbxtensor/ops/batch_norm.py +163 -0
- nbxtensor/ops/binary_strided.py +196 -0
- nbxtensor/ops/bitwise_and.py +26 -0
- nbxtensor/ops/bitwise_left_shift.py +47 -0
- nbxtensor/ops/bitwise_not.py +24 -0
- nbxtensor/ops/bitwise_or.py +26 -0
- nbxtensor/ops/bitwise_right_shift.py +47 -0
- nbxtensor/ops/bucketize.py +37 -0
- nbxtensor/ops/cat_op.py +87 -0
- nbxtensor/ops/celu.py +20 -0
- nbxtensor/ops/clamp.py +24 -0
- nbxtensor/ops/clamp_min.py +22 -0
- nbxtensor/ops/conv2d.py +265 -0
- nbxtensor/ops/conv2d_m8n8k4.py +165 -0
- nbxtensor/ops/conv_depthwise2d.py +154 -0
- nbxtensor/ops/conv_transpose2d.py +148 -0
- nbxtensor/ops/conv_wrappers.py +1482 -0
- nbxtensor/ops/copy.py +18 -0
- nbxtensor/ops/copy_op.py +39 -0
- nbxtensor/ops/cos.py +17 -0
- nbxtensor/ops/count_nonzero.py +64 -0
- nbxtensor/ops/cross_entropy.py +67 -0
- nbxtensor/ops/cummax.py +109 -0
- nbxtensor/ops/cummin.py +105 -0
- nbxtensor/ops/cumsum.py +244 -0
- nbxtensor/ops/decode_attn_vec.py +162 -0
- nbxtensor/ops/depthwise_conv2d.py +132 -0
- nbxtensor/ops/dequant_gemv.py +190 -0
- nbxtensor/ops/dft.py +67 -0
- nbxtensor/ops/dispatch.py +1228 -0
- nbxtensor/ops/div.py +75 -0
- nbxtensor/ops/dot_op.py +67 -0
- nbxtensor/ops/dropout.py +23 -0
- nbxtensor/ops/dtype_convert.py +42 -0
- nbxtensor/ops/elementwise_launch.py +81 -0
- nbxtensor/ops/elementwise_wrappers.py +2315 -0
- nbxtensor/ops/elu.py +20 -0
- nbxtensor/ops/embedding.py +69 -0
- nbxtensor/ops/eq.py +32 -0
- nbxtensor/ops/erf.py +18 -0
- nbxtensor/ops/exp.py +17 -0
- nbxtensor/ops/exp2.py +18 -0
- nbxtensor/ops/fft_op.py +125 -0
- nbxtensor/ops/fill_op.py +20 -0
- nbxtensor/ops/flash_attention.py +307 -0
- nbxtensor/ops/flash_attention_m8n8k4.py +384 -0
- nbxtensor/ops/flash_decode.py +78 -0
- nbxtensor/ops/flip_op.py +60 -0
- nbxtensor/ops/floor.py +90 -0
- nbxtensor/ops/full_op.py +24 -0
- nbxtensor/ops/fused_moe.py +417 -0
- nbxtensor/ops/fused_moe_m8n8k4.py +99 -0
- nbxtensor/ops/fused_upsample_conv.py +413 -0
- nbxtensor/ops/gather_op.py +52 -0
- nbxtensor/ops/ge.py +32 -0
- nbxtensor/ops/gelu.py +31 -0
- nbxtensor/ops/gemv_vec.py +80 -0
- nbxtensor/ops/glu.py +43 -0
- nbxtensor/ops/grid_sampler.py +145 -0
- nbxtensor/ops/group_norm_tiled.py +67 -0
- nbxtensor/ops/groupnorm.py +123 -0
- nbxtensor/ops/gt.py +32 -0
- nbxtensor/ops/hardsigmoid.py +18 -0
- nbxtensor/ops/hardswish.py +19 -0
- nbxtensor/ops/im2col.py +57 -0
- nbxtensor/ops/index_add.py +95 -0
- nbxtensor/ops/index_put_op.py +92 -0
- nbxtensor/ops/index_select.py +121 -0
- nbxtensor/ops/index_wrappers.py +770 -0
- nbxtensor/ops/isfinite.py +20 -0
- nbxtensor/ops/isinf.py +20 -0
- nbxtensor/ops/isnan.py +18 -0
- nbxtensor/ops/launch_keys.py +1054 -0
- nbxtensor/ops/launch_observer.py +54 -0
- nbxtensor/ops/layernorm.py +71 -0
- nbxtensor/ops/le.py +35 -0
- nbxtensor/ops/leaky_relu.py +19 -0
- nbxtensor/ops/lerp.py +76 -0
- nbxtensor/ops/linalg_wrappers.py +1370 -0
- nbxtensor/ops/linspace_op.py +47 -0
- nbxtensor/ops/log.py +17 -0
- nbxtensor/ops/log_sigmoid.py +23 -0
- nbxtensor/ops/logical_and.py +26 -0
- nbxtensor/ops/logical_not.py +24 -0
- nbxtensor/ops/logical_or.py +26 -0
- nbxtensor/ops/logical_xor.py +37 -0
- nbxtensor/ops/loss_wrappers.py +282 -0
- nbxtensor/ops/lt.py +32 -0
- nbxtensor/ops/masked_fill.py +21 -0
- nbxtensor/ops/matmul.py +377 -0
- nbxtensor/ops/matmul_m8n8k4.py +235 -0
- nbxtensor/ops/max_pool2d.py +74 -0
- nbxtensor/ops/max_pool3d.py +93 -0
- nbxtensor/ops/max_reduce.py +91 -0
- nbxtensor/ops/maximum.py +18 -0
- nbxtensor/ops/mean.py +40 -0
- nbxtensor/ops/min_reduce.py +91 -0
- nbxtensor/ops/minimum.py +18 -0
- nbxtensor/ops/mish.py +22 -0
- nbxtensor/ops/mma_m8n8k4.py +152 -0
- nbxtensor/ops/moe_align.py +195 -0
- nbxtensor/ops/moe_decode_vec.py +374 -0
- nbxtensor/ops/moe_device_residency.py +141 -0
- nbxtensor/ops/moe_wrappers.py +345 -0
- nbxtensor/ops/mse_loss.py +79 -0
- nbxtensor/ops/mul.py +51 -0
- nbxtensor/ops/nan_to_num.py +23 -0
- nbxtensor/ops/ne.py +32 -0
- nbxtensor/ops/neg.py +17 -0
- nbxtensor/ops/nllloss.py +124 -0
- nbxtensor/ops/nonzero.py +33 -0
- nbxtensor/ops/nonzero_op.py +62 -0
- nbxtensor/ops/norm_wrappers.py +579 -0
- nbxtensor/ops/ones_op.py +20 -0
- nbxtensor/ops/oracles/__init__.py +6 -0
- nbxtensor/ops/oracles/conv2d_fp64.py +450 -0
- nbxtensor/ops/pad_op.py +159 -0
- nbxtensor/ops/paged_decode_attn.py +215 -0
- nbxtensor/ops/pixel_shuffle.py +144 -0
- nbxtensor/ops/pixel_unshuffle.py +72 -0
- nbxtensor/ops/pow.py +137 -0
- nbxtensor/ops/prod.py +81 -0
- nbxtensor/ops/rand_op.py +52 -0
- nbxtensor/ops/random_wrappers.py +366 -0
- nbxtensor/ops/reciprocal.py +17 -0
- nbxtensor/ops/reduction_wrappers.py +845 -0
- nbxtensor/ops/relu.py +18 -0
- nbxtensor/ops/remainder.py +46 -0
- nbxtensor/ops/repeat_interleave.py +48 -0
- nbxtensor/ops/repeat_op.py +132 -0
- nbxtensor/ops/residual_chain.py +374 -0
- nbxtensor/ops/rmsnorm.py +68 -0
- nbxtensor/ops/rope.py +142 -0
- nbxtensor/ops/rsqrt.py +17 -0
- nbxtensor/ops/scaled_softmax.py +96 -0
- nbxtensor/ops/scatter_op.py +139 -0
- nbxtensor/ops/screen_oracle.py +479 -0
- nbxtensor/ops/selu.py +22 -0
- nbxtensor/ops/shape_wrappers.py +1032 -0
- nbxtensor/ops/sigmoid.py +19 -0
- nbxtensor/ops/silu.py +27 -0
- nbxtensor/ops/sin.py +17 -0
- nbxtensor/ops/softmax.py +169 -0
- nbxtensor/ops/softplus.py +27 -0
- nbxtensor/ops/sort_op.py +304 -0
- nbxtensor/ops/spectral_wrappers.py +481 -0
- nbxtensor/ops/sqrt.py +17 -0
- nbxtensor/ops/stack_op.py +84 -0
- nbxtensor/ops/std.py +113 -0
- nbxtensor/ops/strided_copy.py +200 -0
- nbxtensor/ops/sub.py +35 -0
- nbxtensor/ops/sum.py +40 -0
- nbxtensor/ops/tan.py +19 -0
- nbxtensor/ops/tanh.py +21 -0
- nbxtensor/ops/threshold.py +19 -0
- nbxtensor/ops/tiling.yml +91 -0
- nbxtensor/ops/tiling_policy.py +43 -0
- nbxtensor/ops/tiling_sizes.py +370 -0
- nbxtensor/ops/topk.py +267 -0
- nbxtensor/ops/trace_op.py +64 -0
- nbxtensor/ops/tril.py +64 -0
- nbxtensor/ops/triu.py +62 -0
- nbxtensor/ops/unfold_op.py +37 -0
- nbxtensor/ops/unique_op.py +97 -0
- nbxtensor/ops/upsample_bilinear2d.py +106 -0
- nbxtensor/ops/upsample_nearest2d.py +52 -0
- nbxtensor/ops/upsample_nearest3d.py +61 -0
- nbxtensor/ops/var.py +136 -0
- nbxtensor/ops/vector_norm.py +216 -0
- nbxtensor/ops/weight_norm.py +104 -0
- nbxtensor/ops/where.py +39 -0
- nbxtensor/ops/wrappers.py +825 -0
- nbxtensor/ops/zeros_op.py +20 -0
- nbxtensor/quant/__init__.py +1 -0
- nbxtensor/quant/quantized_tensor.py +187 -0
- nbxtensor/random/__init__.py +1 -0
- nbxtensor/random/mt_randn.py +154 -0
- nbxtensor/random/rng_pin.py +59 -0
- nbxtensor/random/rng_stream.py +118 -0
- nbxtensor/random/seeded_draw.py +223 -0
- nbxtensor/shape/__init__.py +157 -0
- nbxtensor/tensor.py +1867 -0
- nbxtensor-0.0.1.dist-info/METADATA +379 -0
- nbxtensor-0.0.1.dist-info/RECORD +291 -0
- nbxtensor-0.0.1.dist-info/WHEEL +4 -0
- nbxtensor-0.0.1.dist-info/licenses/LICENSE +189 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Max pooling 2D with indices — pure @triton.jit kernel.
|
|
2
|
+
|
|
3
|
+
Extracted from FlagGems max_pool2d_with_indices.py. Forward only (inference).
|
|
4
|
+
2D blocked approach, returns both output values and indices of max elements.
|
|
5
|
+
Uses float('-inf') as min value instead of FlagGems get_dtype_min().
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import triton
|
|
9
|
+
import triton.language as tl
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@triton.jit
|
|
13
|
+
def max_pool2d_forward_kernel(
|
|
14
|
+
input_ptr,
|
|
15
|
+
output_ptr,
|
|
16
|
+
indices_ptr,
|
|
17
|
+
in_stride_n, in_stride_c, in_stride_h, in_stride_w,
|
|
18
|
+
in_c, in_h, in_w,
|
|
19
|
+
out_h, out_w,
|
|
20
|
+
kernel_h: tl.constexpr,
|
|
21
|
+
kernel_w: tl.constexpr,
|
|
22
|
+
stride_h: tl.constexpr,
|
|
23
|
+
stride_w: tl.constexpr,
|
|
24
|
+
padding_h: tl.constexpr,
|
|
25
|
+
padding_w: tl.constexpr,
|
|
26
|
+
dilation_h: tl.constexpr,
|
|
27
|
+
dilation_w: tl.constexpr,
|
|
28
|
+
BLOCK_H: tl.constexpr,
|
|
29
|
+
BLOCK_W: tl.constexpr,
|
|
30
|
+
):
|
|
31
|
+
pid_nc = tl.program_id(0).to(tl.int64)
|
|
32
|
+
pid_hw = tl.program_id(1).to(tl.int64)
|
|
33
|
+
num_w_blocks = tl.cdiv(out_w, BLOCK_W)
|
|
34
|
+
h_block_idx = pid_hw // num_w_blocks
|
|
35
|
+
w_block_idx = pid_hw % num_w_blocks
|
|
36
|
+
n_idx = pid_nc // in_c
|
|
37
|
+
c_idx = pid_nc % in_c
|
|
38
|
+
|
|
39
|
+
h_out_offsets = h_block_idx * BLOCK_H + tl.arange(0, BLOCK_H)
|
|
40
|
+
w_out_offsets = w_block_idx * BLOCK_W + tl.arange(0, BLOCK_W)
|
|
41
|
+
|
|
42
|
+
min_val = float('-inf')
|
|
43
|
+
max_val_acc = tl.full((BLOCK_H, BLOCK_W), min_val, dtype=input_ptr.type.element_ty)
|
|
44
|
+
max_idx_acc = tl.full((BLOCK_H, BLOCK_W), -1, dtype=tl.int64)
|
|
45
|
+
|
|
46
|
+
input_base_ptr = input_ptr + n_idx * in_stride_n + c_idx * in_stride_c
|
|
47
|
+
|
|
48
|
+
for kh in tl.static_range(0, kernel_h):
|
|
49
|
+
for kw in tl.static_range(0, kernel_w):
|
|
50
|
+
h_in = h_out_offsets[:, None] * stride_h - padding_h + kh * dilation_h
|
|
51
|
+
w_in = w_out_offsets[None, :] * stride_w - padding_w + kw * dilation_w
|
|
52
|
+
in_mask = (h_in >= 0) & (h_in < in_h) & (w_in >= 0) & (w_in < in_w)
|
|
53
|
+
input_offset = h_in * in_stride_h + w_in * in_stride_w
|
|
54
|
+
current_val = tl.load(
|
|
55
|
+
input_base_ptr + input_offset, mask=in_mask, other=min_val
|
|
56
|
+
)
|
|
57
|
+
current_idx = h_in * in_w + w_in
|
|
58
|
+
|
|
59
|
+
is_new_max = current_val > max_val_acc
|
|
60
|
+
max_val_acc = tl.where(is_new_max, current_val, max_val_acc)
|
|
61
|
+
max_idx_acc = tl.where(is_new_max & in_mask, current_idx, max_idx_acc)
|
|
62
|
+
|
|
63
|
+
out_base_ptr = output_ptr + pid_nc * out_h * out_w
|
|
64
|
+
indices_base_ptr = indices_ptr + pid_nc * out_h * out_w
|
|
65
|
+
output_block_ptr = (
|
|
66
|
+
out_base_ptr + h_out_offsets[:, None] * out_w + w_out_offsets[None, :]
|
|
67
|
+
)
|
|
68
|
+
indices_block_ptr = (
|
|
69
|
+
indices_base_ptr + h_out_offsets[:, None] * out_w + w_out_offsets[None, :]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
out_mask = (h_out_offsets[:, None] < out_h) & (w_out_offsets[None, :] < out_w)
|
|
73
|
+
tl.store(output_block_ptr, max_val_acc, mask=out_mask)
|
|
74
|
+
tl.store(indices_block_ptr, max_idx_acc, mask=out_mask)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Max pooling 3D with indices — pure @triton.jit kernel.
|
|
2
|
+
|
|
3
|
+
3D (temporal) extension of ops/max_pool2d.py (FlagGems-derived blocked
|
|
4
|
+
kernel): one program per (n, c, t_out) output plane block, with a
|
|
5
|
+
static kt x kh x kw window scan. Forward only (inference). Returns both
|
|
6
|
+
output values and int64 indices of the max elements; indices follow the
|
|
7
|
+
PyTorch max_pool3d_with_indices convention — flattened offset into the
|
|
8
|
+
(T, H, W) volume of each (n, c) plane, ties resolved to the FIRST
|
|
9
|
+
maximum in (t, h, w) window scan order (strict `>` update, same as the
|
|
10
|
+
2D kernel). Uses float('-inf') as the identity like the 2D kernel.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import triton
|
|
14
|
+
import triton.language as tl
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@triton.jit
|
|
18
|
+
def max_pool3d_forward_kernel(
|
|
19
|
+
input_ptr,
|
|
20
|
+
output_ptr,
|
|
21
|
+
indices_ptr,
|
|
22
|
+
in_stride_n, in_stride_c, in_stride_t, in_stride_h, in_stride_w,
|
|
23
|
+
in_c, in_t, in_h, in_w,
|
|
24
|
+
out_t, out_h, out_w,
|
|
25
|
+
kernel_t: tl.constexpr,
|
|
26
|
+
kernel_h: tl.constexpr,
|
|
27
|
+
kernel_w: tl.constexpr,
|
|
28
|
+
stride_t: tl.constexpr,
|
|
29
|
+
stride_h: tl.constexpr,
|
|
30
|
+
stride_w: tl.constexpr,
|
|
31
|
+
padding_t: tl.constexpr,
|
|
32
|
+
padding_h: tl.constexpr,
|
|
33
|
+
padding_w: tl.constexpr,
|
|
34
|
+
dilation_t: tl.constexpr,
|
|
35
|
+
dilation_h: tl.constexpr,
|
|
36
|
+
dilation_w: tl.constexpr,
|
|
37
|
+
BLOCK_H: tl.constexpr,
|
|
38
|
+
BLOCK_W: tl.constexpr,
|
|
39
|
+
):
|
|
40
|
+
pid_nct = tl.program_id(0).to(tl.int64)
|
|
41
|
+
pid_hw = tl.program_id(1).to(tl.int64)
|
|
42
|
+
num_w_blocks = tl.cdiv(out_w, BLOCK_W)
|
|
43
|
+
h_block_idx = pid_hw // num_w_blocks
|
|
44
|
+
w_block_idx = pid_hw % num_w_blocks
|
|
45
|
+
t_out_idx = pid_nct % out_t
|
|
46
|
+
nc_idx = pid_nct // out_t
|
|
47
|
+
n_idx = nc_idx // in_c
|
|
48
|
+
c_idx = nc_idx % in_c
|
|
49
|
+
|
|
50
|
+
h_out_offsets = h_block_idx * BLOCK_H + tl.arange(0, BLOCK_H)
|
|
51
|
+
w_out_offsets = w_block_idx * BLOCK_W + tl.arange(0, BLOCK_W)
|
|
52
|
+
|
|
53
|
+
min_val = float('-inf')
|
|
54
|
+
max_val_acc = tl.full((BLOCK_H, BLOCK_W), min_val, dtype=input_ptr.type.element_ty)
|
|
55
|
+
max_idx_acc = tl.full((BLOCK_H, BLOCK_W), -1, dtype=tl.int64)
|
|
56
|
+
|
|
57
|
+
input_base_ptr = input_ptr + n_idx * in_stride_n + c_idx * in_stride_c
|
|
58
|
+
|
|
59
|
+
for kt in tl.static_range(0, kernel_t):
|
|
60
|
+
t_in = t_out_idx * stride_t - padding_t + kt * dilation_t
|
|
61
|
+
t_ok = (t_in >= 0) & (t_in < in_t)
|
|
62
|
+
for kh in tl.static_range(0, kernel_h):
|
|
63
|
+
for kw in tl.static_range(0, kernel_w):
|
|
64
|
+
h_in = h_out_offsets[:, None] * stride_h - padding_h + kh * dilation_h
|
|
65
|
+
w_in = w_out_offsets[None, :] * stride_w - padding_w + kw * dilation_w
|
|
66
|
+
in_mask = (t_ok
|
|
67
|
+
& (h_in >= 0) & (h_in < in_h)
|
|
68
|
+
& (w_in >= 0) & (w_in < in_w))
|
|
69
|
+
input_offset = (t_in * in_stride_t
|
|
70
|
+
+ h_in * in_stride_h + w_in * in_stride_w)
|
|
71
|
+
current_val = tl.load(
|
|
72
|
+
input_base_ptr + input_offset, mask=in_mask, other=min_val
|
|
73
|
+
)
|
|
74
|
+
# PyTorch convention: flattened (t, h, w) offset per (n, c).
|
|
75
|
+
current_idx = ((t_in * in_h + h_in) * in_w + w_in).to(tl.int64)
|
|
76
|
+
|
|
77
|
+
is_new_max = current_val > max_val_acc
|
|
78
|
+
max_val_acc = tl.where(is_new_max, current_val, max_val_acc)
|
|
79
|
+
max_idx_acc = tl.where(is_new_max & in_mask, current_idx, max_idx_acc)
|
|
80
|
+
|
|
81
|
+
plane = out_h * out_w
|
|
82
|
+
out_base_ptr = output_ptr + pid_nct * plane
|
|
83
|
+
indices_base_ptr = indices_ptr + pid_nct * plane
|
|
84
|
+
output_block_ptr = (
|
|
85
|
+
out_base_ptr + h_out_offsets[:, None] * out_w + w_out_offsets[None, :]
|
|
86
|
+
)
|
|
87
|
+
indices_block_ptr = (
|
|
88
|
+
indices_base_ptr + h_out_offsets[:, None] * out_w + w_out_offsets[None, :]
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
out_mask = (h_out_offsets[:, None] < out_h) & (w_out_offsets[None, :] < out_w)
|
|
92
|
+
tl.store(output_block_ptr, max_val_acc, mask=out_mask)
|
|
93
|
+
tl.store(indices_block_ptr, max_idx_acc, mask=out_mask)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Max reduction — pure @triton.jit kernels.
|
|
2
|
+
|
|
3
|
+
Global two-pass max and dim-specific max (returns value + index).
|
|
4
|
+
Extracted from FlagGems (Apache-2.0).
|
|
5
|
+
|
|
6
|
+
Note: FlagGems uses `get_dtype_min` from utils.limits. We inline the
|
|
7
|
+
sentinel via float('-inf') which Triton handles for all float dtypes.
|
|
8
|
+
For the dim kernel, bf16 accumulation is promoted to fp32.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import triton
|
|
12
|
+
import triton.language as tl
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# Global reduction: two-pass max
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
@triton.jit
|
|
20
|
+
def max_kernel_mid(
|
|
21
|
+
inp,
|
|
22
|
+
mid,
|
|
23
|
+
M,
|
|
24
|
+
BLOCK_SIZE: tl.constexpr,
|
|
25
|
+
):
|
|
26
|
+
"""Pass 1: compute block-local max."""
|
|
27
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
28
|
+
offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
|
|
29
|
+
inp_ptrs = inp + offset
|
|
30
|
+
mask = offset < M
|
|
31
|
+
inp_val = tl.load(inp_ptrs, mask=mask, other=float('-inf'))
|
|
32
|
+
max_val = tl.max(inp_val)
|
|
33
|
+
mid_ptr = mid + pid
|
|
34
|
+
tl.store(mid_ptr, max_val)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@triton.jit
|
|
38
|
+
def max_kernel_result(mid, out, mid_size, BLOCK_MID: tl.constexpr):
|
|
39
|
+
"""Pass 2: reduce partial maxes into scalar."""
|
|
40
|
+
offset = tl.arange(0, BLOCK_MID)
|
|
41
|
+
mid_ptrs = mid + offset
|
|
42
|
+
mask = offset < mid_size
|
|
43
|
+
mid_val = tl.load(mid_ptrs, mask=mask, other=float('-inf'))
|
|
44
|
+
max_val = tl.max(mid_val)
|
|
45
|
+
tl.store(out, max_val)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Dim-specific: max along one axis (returns value + argmax index)
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
@triton.jit
|
|
53
|
+
def max_kernel(
|
|
54
|
+
inp,
|
|
55
|
+
out_value,
|
|
56
|
+
out_index,
|
|
57
|
+
M,
|
|
58
|
+
N,
|
|
59
|
+
BLOCK_M: tl.constexpr,
|
|
60
|
+
BLOCK_N: tl.constexpr,
|
|
61
|
+
):
|
|
62
|
+
"""Max reduction along compressed inner dim N.
|
|
63
|
+
|
|
64
|
+
inp: [M, N] (dim-compressed) → out_value: [M], out_index: [M] (int64)
|
|
65
|
+
Tracks both maximum value and its index (argmax).
|
|
66
|
+
bf16 inputs are accumulated in fp32 to avoid precision issues.
|
|
67
|
+
"""
|
|
68
|
+
pid_m = tl.program_id(0).to(tl.int64)
|
|
69
|
+
m_offset = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
|
70
|
+
|
|
71
|
+
dtype = inp.type.element_ty
|
|
72
|
+
acc_type = tl.float32 if dtype is tl.bfloat16 else dtype
|
|
73
|
+
result_value = tl.full([BLOCK_M], value=float('-inf'), dtype=acc_type)
|
|
74
|
+
result_index = tl.zeros([BLOCK_M], dtype=tl.int64)
|
|
75
|
+
for i in range(0, N, BLOCK_N):
|
|
76
|
+
n_offset = i + tl.arange(0, BLOCK_N)
|
|
77
|
+
offset = m_offset[:, None] * N + n_offset[None, :]
|
|
78
|
+
mask = (m_offset[:, None] < M) & (n_offset[None, :] < N)
|
|
79
|
+
inp_ptrs = inp + offset
|
|
80
|
+
inp_vals = tl.load(inp_ptrs, mask=mask, other=float('-inf'))
|
|
81
|
+
max_value, max_index = tl.max(inp_vals, axis=1, return_indices=True)
|
|
82
|
+
update_mask = max_value > result_value
|
|
83
|
+
result_value = tl.where(update_mask, max_value, result_value)
|
|
84
|
+
result_index = tl.where(update_mask, i + max_index, result_index)
|
|
85
|
+
|
|
86
|
+
mask1 = m_offset < M
|
|
87
|
+
offset_index = m_offset
|
|
88
|
+
out_value_ptrs = out_value + offset_index
|
|
89
|
+
out_index_ptrs = out_index + offset_index
|
|
90
|
+
tl.store(out_value_ptrs, result_value, mask=mask1)
|
|
91
|
+
tl.store(out_index_ptrs, result_index, mask=mask1)
|
nbxtensor/ops/maximum.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Element-wise maximum — pure @triton.jit kernel."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
|
|
6
|
+
@triton.jit
|
|
7
|
+
def maximum_forward_kernel(
|
|
8
|
+
x_ptr, y_ptr, output_ptr,
|
|
9
|
+
n_elements,
|
|
10
|
+
BLOCK_SIZE: tl.constexpr,
|
|
11
|
+
):
|
|
12
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
13
|
+
offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
|
|
14
|
+
mask = offset < n_elements
|
|
15
|
+
|
|
16
|
+
x = tl.load(x_ptr + offset, mask=mask)
|
|
17
|
+
y = tl.load(y_ptr + offset, mask=mask)
|
|
18
|
+
tl.store(output_ptr + offset, tl.maximum(x, y), mask=mask)
|
nbxtensor/ops/mean.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Mean reduction — pure @triton.jit kernel, the row walked in tiles of the certified width."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
|
|
6
|
+
from nbxtensor.ops._configs import nbx_autotune, row_reduce_configs, prune_row_reduce
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@nbx_autotune(configs=row_reduce_configs(), key=['BATCH_BUCKET', 'FEAT_BUCKET'],
|
|
10
|
+
prune_configs_by={'early_config_prune': prune_row_reduce}, cache_results=True)
|
|
11
|
+
@triton.jit
|
|
12
|
+
def mean_forward_kernel(
|
|
13
|
+
input_ptr, output_ptr,
|
|
14
|
+
batch_dim, feat_dim, BATCH_BUCKET, FEAT_BUCKET,
|
|
15
|
+
input_batch_stride, input_feat_stride,
|
|
16
|
+
BLOCK_SIZE_BATCH: tl.constexpr,
|
|
17
|
+
BLOCK_SIZE_FEAT: tl.constexpr,
|
|
18
|
+
):
|
|
19
|
+
"""Mean reduction over the last dimension: input [batch_dim, feat_dim] -> output [batch_dim].
|
|
20
|
+
|
|
21
|
+
The first tile is reduced alone and every further tile combined into it, so a row that fits one
|
|
22
|
+
tile is reduced by exactly the instructions it was before the loop existed.
|
|
23
|
+
"""
|
|
24
|
+
batch_pid = tl.program_id(0).to(tl.int64)
|
|
25
|
+
batch_offset = batch_pid * BLOCK_SIZE_BATCH + tl.arange(0, BLOCK_SIZE_BATCH)
|
|
26
|
+
feat_offset = tl.arange(0, BLOCK_SIZE_FEAT)
|
|
27
|
+
batch_mask = batch_offset < batch_dim
|
|
28
|
+
row_ptr = input_ptr + input_batch_stride * batch_offset[:, None]
|
|
29
|
+
|
|
30
|
+
feat_mask = feat_offset < feat_dim
|
|
31
|
+
inp = tl.load(row_ptr + input_feat_stride * feat_offset[None, :],
|
|
32
|
+
mask=batch_mask[:, None] & feat_mask[None, :], other=0.0).to(tl.float32)
|
|
33
|
+
result = tl.sum(inp, axis=1)
|
|
34
|
+
for start in range(BLOCK_SIZE_FEAT, feat_dim, BLOCK_SIZE_FEAT):
|
|
35
|
+
offs = start + feat_offset
|
|
36
|
+
feat_mask = offs < feat_dim
|
|
37
|
+
inp = tl.load(row_ptr + input_feat_stride * offs[None, :].to(tl.int64),
|
|
38
|
+
mask=batch_mask[:, None] & feat_mask[None, :], other=0.0).to(tl.float32)
|
|
39
|
+
result += tl.sum(inp, axis=1)
|
|
40
|
+
tl.store(output_ptr + batch_offset, result / feat_dim, mask=batch_mask)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Min reduction — pure @triton.jit kernels.
|
|
2
|
+
|
|
3
|
+
Global two-pass min and dim-specific min (returns value + index).
|
|
4
|
+
Extracted from FlagGems (Apache-2.0).
|
|
5
|
+
|
|
6
|
+
Note: FlagGems uses `get_dtype_max` from utils.limits. We inline the
|
|
7
|
+
sentinel via float('inf') which Triton handles for all float dtypes.
|
|
8
|
+
For the dim kernel, bf16 accumulation is promoted to fp32.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import triton
|
|
12
|
+
import triton.language as tl
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# Global reduction: two-pass min
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
@triton.jit
|
|
20
|
+
def min_kernel_mid(
|
|
21
|
+
inp,
|
|
22
|
+
mid,
|
|
23
|
+
M,
|
|
24
|
+
BLOCK_SIZE: tl.constexpr,
|
|
25
|
+
):
|
|
26
|
+
"""Pass 1: compute block-local min."""
|
|
27
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
28
|
+
offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
|
|
29
|
+
inp_ptrs = inp + offset
|
|
30
|
+
mask = offset < M
|
|
31
|
+
inp_val = tl.load(inp_ptrs, mask=mask, other=float('inf'))
|
|
32
|
+
min_val = tl.min(inp_val)
|
|
33
|
+
mid_ptr = mid + pid
|
|
34
|
+
tl.store(mid_ptr, min_val)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@triton.jit
|
|
38
|
+
def min_kernel_result(mid, out, mid_size, BLOCK_MID: tl.constexpr):
|
|
39
|
+
"""Pass 2: reduce partial mins into scalar."""
|
|
40
|
+
offset = tl.arange(0, BLOCK_MID)
|
|
41
|
+
mid_ptrs = mid + offset
|
|
42
|
+
mask = offset < mid_size
|
|
43
|
+
mid_val = tl.load(mid_ptrs, mask=mask, other=float('inf'))
|
|
44
|
+
min_val = tl.min(mid_val)
|
|
45
|
+
tl.store(out, min_val)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Dim-specific: min along one axis (returns value + argmin index)
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
@triton.jit
|
|
53
|
+
def min_kernel(
|
|
54
|
+
inp,
|
|
55
|
+
out_value,
|
|
56
|
+
out_index,
|
|
57
|
+
M,
|
|
58
|
+
N,
|
|
59
|
+
BLOCK_M: tl.constexpr,
|
|
60
|
+
BLOCK_N: tl.constexpr,
|
|
61
|
+
):
|
|
62
|
+
"""Min reduction along compressed inner dim N.
|
|
63
|
+
|
|
64
|
+
inp: [M, N] (dim-compressed) → out_value: [M], out_index: [M] (int64)
|
|
65
|
+
Tracks both minimum value and its index (argmin).
|
|
66
|
+
bf16 inputs are accumulated in fp32 to avoid precision issues.
|
|
67
|
+
"""
|
|
68
|
+
pid_m = tl.program_id(0).to(tl.int64)
|
|
69
|
+
m_offset = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
|
70
|
+
|
|
71
|
+
dtype = inp.type.element_ty
|
|
72
|
+
acc_type = tl.float32 if dtype is tl.bfloat16 else dtype
|
|
73
|
+
min_values = tl.full([BLOCK_M], dtype=acc_type, value=float('inf'))
|
|
74
|
+
argmin_values = tl.full([BLOCK_M], dtype=tl.int64, value=0)
|
|
75
|
+
for start_n in range(0, N, BLOCK_N):
|
|
76
|
+
n_offset = start_n + tl.arange(0, BLOCK_N)
|
|
77
|
+
offset = m_offset[:, None] * N + n_offset[None, :]
|
|
78
|
+
mask = (m_offset[:, None] < M) & (n_offset[None, :] < N)
|
|
79
|
+
inp_ptrs = inp + offset
|
|
80
|
+
inp_vals = tl.load(inp_ptrs, mask=mask, other=float('inf'))
|
|
81
|
+
local_min, local_argmin = tl.min(inp_vals, 1, return_indices=True)
|
|
82
|
+
update = local_min < min_values
|
|
83
|
+
min_values = tl.where(update, local_min, min_values)
|
|
84
|
+
argmin_values = tl.where(update, start_n + local_argmin, argmin_values)
|
|
85
|
+
|
|
86
|
+
offset_index = m_offset
|
|
87
|
+
out_value_ptrs = out_value + offset_index
|
|
88
|
+
out_index_ptrs = out_index + offset_index
|
|
89
|
+
mask1 = m_offset < M
|
|
90
|
+
tl.store(out_value_ptrs, min_values, mask=mask1)
|
|
91
|
+
tl.store(out_index_ptrs, argmin_values, mask=mask1)
|
nbxtensor/ops/minimum.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Element-wise minimum — pure @triton.jit kernel."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
|
|
6
|
+
@triton.jit
|
|
7
|
+
def minimum_forward_kernel(
|
|
8
|
+
x_ptr, y_ptr, output_ptr,
|
|
9
|
+
n_elements,
|
|
10
|
+
BLOCK_SIZE: tl.constexpr,
|
|
11
|
+
):
|
|
12
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
13
|
+
offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
|
|
14
|
+
mask = offset < n_elements
|
|
15
|
+
|
|
16
|
+
x = tl.load(x_ptr + offset, mask=mask)
|
|
17
|
+
y = tl.load(y_ptr + offset, mask=mask)
|
|
18
|
+
tl.store(output_ptr + offset, tl.minimum(x, y), mask=mask)
|
nbxtensor/ops/mish.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Mish — pure @triton.jit kernel."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
|
|
6
|
+
from ._common import tanh_fn
|
|
7
|
+
|
|
8
|
+
@triton.jit
|
|
9
|
+
def mish_forward_kernel(
|
|
10
|
+
input_ptr, output_ptr,
|
|
11
|
+
n_elements,
|
|
12
|
+
BLOCK_SIZE: tl.constexpr,
|
|
13
|
+
):
|
|
14
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
15
|
+
offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
|
|
16
|
+
mask = offset < n_elements
|
|
17
|
+
|
|
18
|
+
x = tl.load(input_ptr + offset, mask=mask)
|
|
19
|
+
x_fp32 = x.to(tl.float32)
|
|
20
|
+
# mish = x * tanh(softplus(x)) = x * tanh(ln(1 + exp(x)))
|
|
21
|
+
out = x_fp32 * tanh_fn(tl.log(1.0 + tl.exp(x_fp32)))
|
|
22
|
+
tl.store(output_ptr + offset, out, mask=mask)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""The m8n8k4 matrix unit (sm_70 tensor cores, SASS HMMA.884) as a Gluon building block.
|
|
2
|
+
|
|
3
|
+
Triton >= 3.3 lowers `tl.dot` to scalar FMA below sm_80 (triton-lang/triton#5066), so an arch whose profile
|
|
4
|
+
declares `matrix_unit.shape: m8n8k4` gets its tensor cores from here: our own Gluon kernels issue
|
|
5
|
+
`mma.sync.aligned.m8n8k4.row.col.f32.f16.f16.f32` through `inline_asm_elementwise`, with the operands placed in
|
|
6
|
+
registers by linear layouts. Method and proofs: docs/reference/tensor-cores-through-linear-layouts.md.
|
|
7
|
+
|
|
8
|
+
The fragment (CUTLASS cute/atom/mma_traits_sm70.hpp, SM70_8x8x4_F32F16F16F32_TN): lane = a0 + 2 a1 + 4 q0 + 8 q1
|
|
9
|
+
+ 16 b, thread t = a0 + 2 a1 + 4 b of quadpair q holds A row t and B column t (k = 0..3), and accumulator
|
|
10
|
+
register v = v0 + 2 v1 + 4 v2 holds D[m = a0 + 2 v1 + 4 b, n = v0 + 2 a1 + 4 v2]; the four quadpairs tile 16x16.
|
|
11
|
+
|
|
12
|
+
A [BM, BN] accumulator lives as the 7-D tensor [MB, 2, 8, NB, 2, 2, 4] = (mb, mi3, mi2..0, nb, ni3, ni2, ni1..0),
|
|
13
|
+
m = 16 mb + mi, n = 16 nb + ni (row-major order, so a reshape to [BM, BN] moves nothing). In these coordinates the
|
|
14
|
+
A fragment depends only on (mb, mi3, mi2..0, ni1..0) and the B fragment only on (mi2..0, nb, ni3, ni1..0): each is
|
|
15
|
+
a plain broadcast of [MB, 2, 8, 1, 1, 1, 4] / [1, 1, 8, NB, 2, 1, 4], which `inline_asm_elementwise` broadcasts
|
|
16
|
+
itself, so one register fragment feeds every sub-tile that shares it. Operands come from shared memory laid out
|
|
17
|
+
[k-step x rows, 4] (k innermost, 8 bytes per row) and are read in a linear layout derived from the accumulator's;
|
|
18
|
+
the reshape/permute to the broadcast shape is a relabelling, which `convert_layout(assert_trivial=True)` proves.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from triton.experimental import gluon
|
|
22
|
+
from triton.experimental.gluon import language as gl
|
|
23
|
+
|
|
24
|
+
#: What the instruction below takes, as a hardware profile's `matrix_unit` names it (checked by
|
|
25
|
+
#: `_configs.matrix_unit`: a profile cannot declare dtypes this instruction does not compute in). `k` is the
|
|
26
|
+
#: columns one instruction reduces: the split flash's `block_k` is a multiple of it (KSA/KSB count instructions).
|
|
27
|
+
UNIT = {"shape": "m8n8k4", "operand_dtype": "float16", "accumulator_dtype": "float32", "k": 4}
|
|
28
|
+
|
|
29
|
+
MMA_ASM: gl.constexpr = gl.constexpr(
|
|
30
|
+
"mma.sync.aligned.m8n8k4.row.col.f32.f16.f16.f32 {$0,$1,$2,$3,$4,$5,$6,$7}, {$8, $9}, {$12, $13},"
|
|
31
|
+
" {$16,$17,$18,$19,$20,$21,$22,$23};")
|
|
32
|
+
# pack=8: eight accumulator elements per call; f16 operands arrive two per 32-bit register, so A and B are four
|
|
33
|
+
# registers each of which the instruction reads the first two (k = 0..3; the other two repeat them).
|
|
34
|
+
MMA_CONSTRAINTS: gl.constexpr = gl.constexpr(",".join(["=f"] * 8 + ["r"] * 8 + ["f"] * 8))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _bits(n: int):
|
|
38
|
+
return [1 << i for i in range(n.bit_length() - 1)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _e7(d: int, x: int):
|
|
42
|
+
v = [0] * 7
|
|
43
|
+
v[d] = x
|
|
44
|
+
return v
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def acc_layout(MB: int, NB: int, WM: int, WN: int):
|
|
48
|
+
"""The [MB, 2, 8, NB, 2, 2, 4] accumulator over WM x WN warps (WM | MB, WN | NB, all powers of two)."""
|
|
49
|
+
rm = [x * WM for x in _bits(MB // WM)]
|
|
50
|
+
rn = [x * WN for x in _bits(NB // WN)]
|
|
51
|
+
return gl.DistributedLinearLayout(
|
|
52
|
+
reg_bases=[_e7(6, 1), _e7(2, 2), _e7(5, 1)] + [_e7(3, x) for x in rn] + [_e7(0, x) for x in rm],
|
|
53
|
+
lane_bases=[_e7(2, 1), _e7(6, 2), _e7(1, 1), _e7(4, 1), _e7(2, 4)],
|
|
54
|
+
warp_bases=[_e7(0, x) for x in _bits(WM)] + [_e7(3, x) for x in _bits(WN)],
|
|
55
|
+
block_bases=[], shape=[MB, 2, 8, NB, 2, 2, 4])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def frag_a_layout(MB: int, NB: int, WM: int, WN: int):
|
|
59
|
+
"""One k-step of the A operand, [16 MB, 4] = (row, k), in the registers acc_layout(MB, NB, WM, WN) needs."""
|
|
60
|
+
rm = [x * WM for x in _bits(MB // WM)]
|
|
61
|
+
rn = [x * WN for x in _bits(NB // WN)]
|
|
62
|
+
return gl.DistributedLinearLayout(
|
|
63
|
+
reg_bases=[[0, 1], [0, 2], [0, 0]] + [[0, 0]] * len(rn) + [[16 * x, 0] for x in rm],
|
|
64
|
+
lane_bases=[[1, 0], [2, 0], [8, 0], [0, 0], [4, 0]],
|
|
65
|
+
warp_bases=[[16 * x, 0] for x in _bits(WM)] + [[0, 0]] * len(_bits(WN)),
|
|
66
|
+
block_bases=[], shape=[16 * MB, 4])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def frag_b_layout(MB: int, NB: int, WM: int, WN: int):
|
|
70
|
+
"""One k-step of the B operand, [16 NB, 4] = (column, k), in the registers acc_layout(MB, NB, WM, WN) needs."""
|
|
71
|
+
rm = [x * WM for x in _bits(MB // WM)]
|
|
72
|
+
rn = [x * WN for x in _bits(NB // WN)]
|
|
73
|
+
return gl.DistributedLinearLayout(
|
|
74
|
+
reg_bases=[[0, 1], [0, 2], [0, 0]] + [[16 * x, 0] for x in rn] + [[0, 0]] * len(rm),
|
|
75
|
+
lane_bases=[[1, 0], [2, 0], [0, 0], [8, 0], [4, 0]],
|
|
76
|
+
warp_bases=[[0, 0]] * len(_bits(WM)) + [[16 * x, 0] for x in _bits(WN)],
|
|
77
|
+
block_bases=[], shape=[16 * NB, 4])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@gluon.jit
|
|
81
|
+
def acc_rows_cols(L: gl.constexpr, MB: gl.constexpr, NB: gl.constexpr):
|
|
82
|
+
"""Row and column index of every accumulator element, broadcastable over L ([.., 1, 1, 1, 1] / [1, 1, 1, ..])."""
|
|
83
|
+
o0 = gl.arange(0, MB, layout=gl.SliceLayout(1, gl.SliceLayout(2, gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L)))))))
|
|
84
|
+
o1 = gl.arange(0, 2, layout=gl.SliceLayout(0, gl.SliceLayout(2, gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L)))))))
|
|
85
|
+
o2 = gl.arange(0, 8, layout=gl.SliceLayout(0, gl.SliceLayout(1, gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L)))))))
|
|
86
|
+
o3 = gl.arange(0, NB, layout=gl.SliceLayout(0, gl.SliceLayout(1, gl.SliceLayout(2, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L)))))))
|
|
87
|
+
o4 = gl.arange(0, 2, layout=gl.SliceLayout(0, gl.SliceLayout(1, gl.SliceLayout(2, gl.SliceLayout(3, gl.SliceLayout(5, gl.SliceLayout(6, L)))))))
|
|
88
|
+
o5 = gl.arange(0, 2, layout=gl.SliceLayout(0, gl.SliceLayout(1, gl.SliceLayout(2, gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(6, L)))))))
|
|
89
|
+
o6 = gl.arange(0, 4, layout=gl.SliceLayout(0, gl.SliceLayout(1, gl.SliceLayout(2, gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, L)))))))
|
|
90
|
+
rows = o0[:, None, None, None, None, None, None] * 16 + o1[None, :, None, None, None, None, None] * 8 \
|
|
91
|
+
+ o2[None, None, :, None, None, None, None]
|
|
92
|
+
cols = o3[None, None, None, :, None, None, None] * 16 + o4[None, None, None, None, :, None, None] * 8 \
|
|
93
|
+
+ o5[None, None, None, None, None, :, None] * 4 + o6[None, None, None, None, None, None, :]
|
|
94
|
+
return rows, cols
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@gluon.jit
|
|
98
|
+
def row_layout_rows(L: gl.constexpr, MB: gl.constexpr):
|
|
99
|
+
"""Row index [MB, 2, 8] in the layout of a row statistic of L (L with its four column dims reduced)."""
|
|
100
|
+
RS: gl.constexpr = gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L))))
|
|
101
|
+
r0 = gl.arange(0, MB, layout=gl.SliceLayout(1, gl.SliceLayout(2, RS)))
|
|
102
|
+
r1 = gl.arange(0, 2, layout=gl.SliceLayout(0, gl.SliceLayout(2, RS)))
|
|
103
|
+
r2 = gl.arange(0, 8, layout=gl.SliceLayout(0, gl.SliceLayout(1, RS)))
|
|
104
|
+
return r0[:, None, None] * 16 + r1[None, :, None] * 8 + r2[None, None, :]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@gluon.jit
|
|
108
|
+
def row_max(x):
|
|
109
|
+
return gl.max(gl.max(gl.max(gl.max(x, axis=6), axis=5), axis=4), axis=3)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@gluon.jit
|
|
113
|
+
def row_sum(x):
|
|
114
|
+
return gl.sum(gl.sum(gl.sum(gl.sum(x, axis=6), axis=5), axis=4), axis=3)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@gluon.jit
|
|
118
|
+
def bcast_rows(r, L: gl.constexpr):
|
|
119
|
+
"""A [MB, 2, 8] row statistic -> broadcastable over accumulator L (any NB; the row bits are shared)."""
|
|
120
|
+
RL: gl.constexpr = gl.SliceLayout(3, gl.SliceLayout(4, gl.SliceLayout(5, gl.SliceLayout(6, L))))
|
|
121
|
+
r = gl.convert_layout(r, RL, assert_trivial=True)
|
|
122
|
+
return gl.expand_dims(gl.expand_dims(gl.expand_dims(gl.expand_dims(r, 3), 4), 5), 6)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@gluon.jit
|
|
126
|
+
def stage_k_inner(smem, tile, R: gl.constexpr, KK: gl.constexpr):
|
|
127
|
+
"""tile [R, KK] whose k runs along its columns -> smem [(KK/4) R, 4] laid out [k-step, R, 4]."""
|
|
128
|
+
smem.store(gl.reshape(gl.permute(gl.reshape(tile, [R, KK // 4, 4]), [1, 0, 2]), [(KK // 4) * R, 4]))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@gluon.jit
|
|
132
|
+
def stage_k_outer(smem, tile, KK: gl.constexpr, C: gl.constexpr):
|
|
133
|
+
"""tile [KK, C] whose k runs along its rows -> smem [(KK/4) C, 4] laid out [k-step, C, 4]. Conflict-free
|
|
134
|
+
stores want each thread to hold 4 consecutive k of a column (a [4, x] size_per_thread load layout)."""
|
|
135
|
+
smem.store(gl.reshape(gl.permute(gl.reshape(tile, [KK // 4, 4, C]), [0, 2, 1]), [(KK // 4) * C, 4]))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@gluon.jit
|
|
139
|
+
def mma_k4(sa, a_row0, sb, b_row0, acc, MB: gl.constexpr, NB: gl.constexpr,
|
|
140
|
+
LA: gl.constexpr, LB: gl.constexpr, L: gl.constexpr):
|
|
141
|
+
"""acc += A[:, 4j:4j+4] @ B[4j:4j+4, :] for the k-step whose rows start at a_row0 / b_row0 of the staged
|
|
142
|
+
operands (16 MB rows of sa, 16 NB rows of sb)."""
|
|
143
|
+
fa = sa.slice(a_row0, 16 * MB).load(LA) # (mb, mi3, mi2, ni1, mi0 | mi1, ni0)
|
|
144
|
+
fa = gl.reshape(fa, [MB, 2, 2, 2, 2, 2, 2])
|
|
145
|
+
fa = gl.convert_layout(gl.reshape(gl.permute(fa, [0, 1, 2, 5, 4, 3, 6]), [MB, 2, 8, 1, 1, 1, 4]), L,
|
|
146
|
+
assert_trivial=True)
|
|
147
|
+
fb = sb.slice(b_row0, 16 * NB).load(LB) # (nb, ni3, mi2, ni1, mi0 | mi1, ni0)
|
|
148
|
+
fb = gl.reshape(fb, [NB, 2, 2, 2, 2, 2, 2])
|
|
149
|
+
fb = gl.convert_layout(gl.reshape(gl.permute(fb, [2, 5, 4, 0, 1, 3, 6]), [1, 1, 8, NB, 2, 1, 4]), L,
|
|
150
|
+
assert_trivial=True)
|
|
151
|
+
return gl.inline_asm_elementwise(MMA_ASM, MMA_CONSTRAINTS, [fa, fb, acc], dtype=gl.float32, is_pure=True,
|
|
152
|
+
pack=8)
|