tilelang_kernels.py 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873
  1. # SPDX-License-Identifier: Apache-2.0
  2. # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
  3. from __future__ import annotations
  4. import math
  5. from dataclasses import dataclass
  6. from functools import cache
  7. from typing import Any
  8. import torch
  9. from vllm.model_executor.warmup.jit_warmup import WarmupIntRange, zip_inputs
  10. from vllm.model_executor.warmup.jit_warmup_tilelang_helper import (
  11. TileLangLaunchSpec,
  12. VllmTileLangJitKernel,
  13. kernel_launcher,
  14. make_tilelang_warmup_tensor,
  15. )
  16. from vllm.platforms import current_platform
  17. from vllm.tilelang_utils import T, tilelang, tilelang_jit
  18. from vllm.utils.math_utils import cdiv
  19. ENABLE_PDL = current_platform.is_arch_support_pdl() and current_platform.is_cuda()
  20. # TileLang's HIP AllReduce shuffle phase ignores a nonzero reduction-group
  21. # offset, so the 64-thread RMSNorm reduction must be wave64-aligned and use
  22. # threads 0..63.
  23. _MIX_ON_TRAILING_THREADS = current_platform.is_rocm()
  24. def _is_mix_thread():
  25. if _MIX_ON_TRAILING_THREADS:
  26. return T.get_thread_binding() >= 64
  27. return T.get_thread_binding() < 32
  28. def _is_collapse_thread():
  29. if _MIX_ON_TRAILING_THREADS:
  30. return T.get_thread_binding() < 64
  31. return T.get_thread_binding() >= 32
  32. @cache
  33. def compute_num_split(block_k: int, k: int | None, grid_size: int) -> int:
  34. device_props = torch.cuda.get_device_properties(0)
  35. n_sms = device_props.multi_processor_count
  36. split_k = n_sms // grid_size
  37. if k is not None:
  38. # avoid split_k for small k
  39. num_block_k = cdiv(k, block_k)
  40. split_k = min(split_k, num_block_k // 4)
  41. split_k = max(split_k, 1)
  42. return split_k
  43. # ``mhc_fused_tilelang`` splits the hidden size across a fixed thread block, so
  44. # every (tile_n, n_thr) pair is one more kernel to compile. Tuned on GB300
  45. # (hc_mult 4, hidden_size 5120 and 7168) against the separate post + split-k
  46. # GEMM it replaces; the bands are coarse because the loss against a per-shape
  47. # optimum is ~1-2%, while one fixed config gives up to 18% at the top of the
  48. # range.
  49. _FUSED_POST_PRE_N_THR = 128
  50. _FUSED_POST_PRE_MAX_TOKENS = int(__import__("os").environ.get("VLLM_MHC_FUSED_MAX_TOKENS", "32"))
  51. def mhc_fused_post_pre_split_config(
  52. num_tokens: int, hidden_size: int, hc_mult: int
  53. ) -> tuple[int, int, int] | None:
  54. """Pick ``(tile_n, n_splits, n_thr)`` for the fused post + pre-norm GEMM.
  55. Returns None when a separate post kernel followed by a split-k GEMM is the
  56. better choice, or when the hidden size does not divide evenly across the
  57. fused kernel's block. One source of truth for the dispatch decision, the
  58. compile key and the launch, which must agree.
  59. """
  60. if num_tokens > _FUSED_POST_PRE_MAX_TOKENS:
  61. return None
  62. n_thr = _FUSED_POST_PRE_N_THR
  63. if hidden_size % n_thr:
  64. return None
  65. # The projection tiles supply the parallelism a small token count cannot,
  66. # so the tile grows with the batch: 1.3x at one token and 1.03x at 32
  67. # against a split-k GEMM given the split its own estimator computes.
  68. if num_tokens < 16:
  69. tile_n, n_splits = 2, 8
  70. else:
  71. tile_n, n_splits = 6, 8
  72. # The kernel drops projection tiles and k-slices it cannot fill evenly.
  73. mix_size = hc_mult * (hc_mult + 2)
  74. while tile_n > 1 and mix_size % tile_n:
  75. tile_n -= 1
  76. while n_splits > 1 and hidden_size % (n_splits * n_thr):
  77. n_splits //= 2
  78. return tile_n, n_splits, n_thr
  79. def require_fused_post_pre_config(
  80. num_tokens: int, hidden_size: int, hc_mult: int
  81. ) -> tuple[int, int, int]:
  82. """The config for a shape the caller has already committed to fusing."""
  83. config = mhc_fused_post_pre_split_config(num_tokens, hidden_size, hc_mult)
  84. if config is None:
  85. raise ValueError(
  86. "the fused mHC post + pre-norm GEMM does not cover num_tokens="
  87. f"{num_tokens} at hidden_size={hidden_size}"
  88. )
  89. return config
  90. def mhc_fused_post_pre_splits(hidden_size: int, hc_mult: int) -> tuple[int, ...]:
  91. """Every split-k factor the fused post + pre-norm GEMM path can pick."""
  92. configs = (
  93. mhc_fused_post_pre_split_config(num_tokens, hidden_size, hc_mult)
  94. for num_tokens in range(1, _FUSED_POST_PRE_MAX_TOKENS + 1)
  95. )
  96. return tuple(sorted({config[1] for config in configs if config is not None}))
  97. @tilelang_jit
  98. def mhc_pre_big_fuse_tilelang(
  99. gemm_out_mul,
  100. gemm_out_sqrsum,
  101. hc_scale,
  102. hc_base,
  103. residual,
  104. post_mix,
  105. comb_mix,
  106. layer_input,
  107. pre_mix_in,
  108. pre_mix_out,
  109. aux_out,
  110. hidden_size: int,
  111. rms_eps: float,
  112. hc_pre_eps: float,
  113. hc_sinkhorn_eps: float,
  114. hc_post_mult_value: float,
  115. sinkhorn_repeat: int,
  116. n_splits: int = 16,
  117. hc_mult: int = 4,
  118. use_pre_mix_in: bool = False,
  119. save_pre_mix: bool = False,
  120. rms_numel: int = 0,
  121. write_aux: bool = False,
  122. ):
  123. """Fuse coefficient generation and residual collapse after the projection.
  124. With save_pre_mix, store the new pre-mix and collapse with pre_mix_in,
  125. or select stream zero when use_pre_mix_in is false.
  126. """
  127. num_tokens = T.dynamic("num_tokens")
  128. hc_mult3 = hc_mult * (2 + hc_mult)
  129. hidden_block = math.gcd(512, hidden_size)
  130. if rms_numel == 0:
  131. rms_numel = hc_mult * hidden_size
  132. gemm_out_mul: T.Tensor[[n_splits, num_tokens, hc_mult3], T.float32] # type: ignore[no-redef, valid-type]
  133. gemm_out_sqrsum: T.Tensor[[n_splits, num_tokens], T.float32] # type: ignore[no-redef, valid-type]
  134. hc_scale: T.Tensor[[3], T.float32] # type: ignore[no-redef, valid-type]
  135. hc_base: T.Tensor[[hc_mult3], T.float32] # type: ignore[no-redef, valid-type]
  136. residual: T.Tensor[[num_tokens, hc_mult, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  137. # outputs
  138. post_mix: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  139. comb_mix: T.Tensor[[num_tokens, hc_mult * hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  140. layer_input: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  141. pre_mix_in: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  142. pre_mix_out: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  143. aux_out: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  144. with T.Kernel(num_tokens, threads=96) as i:
  145. if ENABLE_PDL:
  146. T.pdl_sync()
  147. ##################################################################
  148. # _pre_norm_fn_fwd_norm
  149. rms = T.alloc_fragment(1, T.float32)
  150. mixes = T.alloc_fragment(hc_mult3, T.float32)
  151. T.clear(mixes)
  152. rms[0] = 0
  153. for i_split in T.serial(n_splits):
  154. rms[0] += gemm_out_sqrsum[i_split, i]
  155. rms[0] = T.rsqrt(rms[0] / rms_numel + rms_eps)
  156. for j in T.Parallel(hc_mult3):
  157. mixes[j] = 0
  158. for i_split in T.serial(n_splits):
  159. mixes[j] += gemm_out_mul[i_split, i, j]
  160. mixes[j] *= rms[0]
  161. mixes_shared = T.alloc_shared(hc_mult3, T.float32)
  162. T.copy(mixes, mixes_shared)
  163. if T.get_thread_binding() < 32:
  164. ##################################################################
  165. # _pre_split_mixes_fwd (post & comb)
  166. cm = T.alloc_fragment((hc_mult, hc_mult), T.float32)
  167. for j in T.Parallel(hc_mult):
  168. if save_pre_mix:
  169. pre_mix_out[i, j] = (
  170. T.sigmoid(mixes_shared[j] * hc_scale[0] + hc_base[j])
  171. + hc_pre_eps
  172. )
  173. post_mix[i, j] = (
  174. T.sigmoid(
  175. mixes_shared[j + hc_mult] * hc_scale[1] + hc_base[j + hc_mult]
  176. )
  177. * hc_post_mult_value
  178. )
  179. for j, k in T.Parallel(hc_mult, hc_mult):
  180. cm[j, k] = (
  181. mixes_shared[j * hc_mult + k + hc_mult * 2] * hc_scale[2]
  182. + hc_base[j * hc_mult + k + hc_mult * 2]
  183. )
  184. ##################################################################
  185. # _sinkhorn_fwd
  186. row_sum = T.alloc_fragment(hc_mult, T.float32)
  187. col_sum = T.alloc_fragment(hc_mult, T.float32)
  188. # comb = comb.softmax(-1) + eps
  189. row_max = T.alloc_fragment(hc_mult, T.float32)
  190. T.reduce_max(cm, row_max, dim=1)
  191. for j, k in T.Parallel(hc_mult, hc_mult):
  192. cm[j, k] = T.exp(cm[j, k] - row_max[j])
  193. T.reduce_sum(cm, row_sum, dim=1)
  194. for j, k in T.Parallel(hc_mult, hc_mult):
  195. cm[j, k] = cm[j, k] / row_sum[j] + hc_sinkhorn_eps
  196. # comb = comb / (comb.sum(-2) + eps)
  197. T.reduce_sum(cm, col_sum, dim=0)
  198. for j, k in T.Parallel(hc_mult, hc_mult):
  199. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  200. for _ in T.serial(sinkhorn_repeat - 1):
  201. # comb = comb / (comb.sum(-1) + eps)
  202. T.reduce_sum(cm, row_sum, dim=1)
  203. for j, k in T.Parallel(hc_mult, hc_mult):
  204. cm[j, k] = cm[j, k] / (row_sum[j] + hc_sinkhorn_eps)
  205. # comb = comb / (comb.sum(-2) + eps)
  206. T.reduce_sum(cm, col_sum, dim=0)
  207. for j, k in T.Parallel(hc_mult, hc_mult):
  208. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  209. # save comb_mix to global memory
  210. for j, k in T.Parallel(hc_mult, hc_mult):
  211. comb_mix[i, j * hc_mult + k] = cm[j, k]
  212. else:
  213. ##################################################################
  214. # _pre_split_mixes_fwd (pre)
  215. pre_mix_shared = T.alloc_shared(hc_mult, T.float32)
  216. for j in T.Parallel(hc_mult):
  217. if use_pre_mix_in:
  218. pre_mix_shared[j] = pre_mix_in[i, j]
  219. elif save_pre_mix:
  220. pre_mix_shared[j] = T.if_then_else(j == 0, 1.0, 0.0)
  221. else:
  222. pre_mix_shared[j] = (
  223. T.sigmoid(mixes_shared[j] * hc_scale[0] + hc_base[j])
  224. + hc_pre_eps
  225. )
  226. ###################################################################
  227. # _pre_apply_mix_fwd
  228. for i0_h in T.Pipelined(hidden_size // hidden_block, num_stages=2):
  229. xs = T.alloc_shared((hc_mult, hidden_block), T.float32)
  230. xl = T.alloc_fragment((hc_mult, hidden_block), T.float32)
  231. T.copy(residual[i, 0, i0_h * hidden_block], xs)
  232. T.copy(xs, xl)
  233. ol = T.alloc_fragment(hidden_block, T.float32)
  234. T.clear(ol)
  235. if write_aux:
  236. # Aux consumers (draft models) take the plain stream mean
  237. # of the same residual this collapse reads.
  238. aux = T.alloc_fragment(hidden_block, T.float32)
  239. T.clear(aux)
  240. for i_hc in T.serial(hc_mult):
  241. pre = pre_mix_shared[i_hc]
  242. for i1_h in T.Parallel(hidden_block):
  243. ol[i1_h] += pre * xl[i_hc, i1_h]
  244. if write_aux:
  245. aux[i1_h] += xl[i_hc, i1_h]
  246. if write_aux:
  247. for i1_h in T.Parallel(hidden_block):
  248. aux_out[i, i0_h * hidden_block + i1_h] = T.bfloat16(
  249. aux[i1_h] / hc_mult
  250. )
  251. T.copy(ol, layer_input[i, i0_h * hidden_block])
  252. if ENABLE_PDL:
  253. T.pdl_trigger()
  254. # Copied from https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/layers/mhc.py#L478
  255. @tilelang_jit
  256. def mhc_pre_big_fuse_with_norm_tilelang(
  257. gemm_out_mul,
  258. gemm_out_sqrsum,
  259. hc_scale,
  260. hc_base,
  261. residual,
  262. post_mix,
  263. comb_mix,
  264. layer_input,
  265. norm_weight,
  266. pre_mix_in,
  267. pre_mix_out,
  268. aux_out,
  269. hidden_size: int,
  270. rms_eps: float,
  271. hc_pre_eps: float,
  272. hc_sinkhorn_eps: float,
  273. hc_post_mult_value: float,
  274. sinkhorn_repeat: int,
  275. norm_eps: float,
  276. n_splits: int = 16,
  277. hc_mult: int = 4,
  278. gemm_last_dim: int = -1,
  279. use_pre_mix_in: bool = False,
  280. save_pre_mix: bool = False,
  281. rms_numel: int = 0,
  282. split_mode: str = "fused",
  283. write_aux: bool = False,
  284. ):
  285. # Split modes require shifted mHC: input collapse uses a carried pre-mix.
  286. assert split_mode in ("fused", "stats", "input")
  287. assert split_mode == "fused" or save_pre_mix
  288. num_tokens = T.dynamic("num_tokens")
  289. hc_mult3 = hc_mult * (2 + hc_mult)
  290. if gemm_last_dim < 0:
  291. gemm_last_dim = hc_mult3
  292. if rms_numel == 0:
  293. rms_numel = hc_mult * hidden_size
  294. hidden_block = math.gcd(1024, hidden_size)
  295. gemm_out_mul: T.Tensor[[n_splits, num_tokens, gemm_last_dim], T.float32] # type: ignore[no-redef, valid-type]
  296. gemm_out_sqrsum: T.Tensor[[n_splits, num_tokens], T.float32] # type: ignore[no-redef, valid-type]
  297. hc_scale: T.Tensor[[3], T.float32] # type: ignore[no-redef, valid-type]
  298. hc_base: T.Tensor[[hc_mult3], T.float32] # type: ignore[no-redef, valid-type]
  299. residual: T.Tensor[[num_tokens, hc_mult, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  300. post_mix: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  301. comb_mix: T.Tensor[[num_tokens, hc_mult * hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  302. layer_input: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  303. norm_weight: T.Tensor[[hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  304. pre_mix_in: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  305. pre_mix_out: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  306. aux_out: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  307. with T.Kernel(num_tokens, threads=96) as i:
  308. if ENABLE_PDL:
  309. T.pdl_sync()
  310. mixes_shared = T.alloc_shared(hc_mult3, T.float32)
  311. if split_mode != "input":
  312. rms = T.alloc_fragment(1, T.float32)
  313. mixes = T.alloc_fragment(hc_mult3, T.float32)
  314. T.clear(mixes)
  315. rms[0] = 0
  316. for i_split in T.serial(n_splits):
  317. rms[0] += gemm_out_sqrsum[i_split, i]
  318. rms[0] = T.rsqrt(rms[0] / rms_numel + rms_eps)
  319. for j in T.Parallel(hc_mult3):
  320. mixes[j] = 0
  321. for i_split in T.serial(n_splits):
  322. mixes[j] += gemm_out_mul[i_split, i, j]
  323. mixes[j] *= rms[0]
  324. T.copy(mixes, mixes_shared)
  325. if split_mode != "input" and _is_mix_thread():
  326. cm = T.alloc_fragment((hc_mult, hc_mult), T.float32)
  327. for j in T.Parallel(hc_mult):
  328. if save_pre_mix:
  329. pre_mix_out[i, j] = (
  330. T.sigmoid(mixes_shared[j] * hc_scale[0] + hc_base[j])
  331. + hc_pre_eps
  332. )
  333. post_mix[i, j] = (
  334. T.sigmoid(
  335. mixes_shared[j + hc_mult] * hc_scale[1] + hc_base[j + hc_mult]
  336. )
  337. * hc_post_mult_value
  338. )
  339. for j, k in T.Parallel(hc_mult, hc_mult):
  340. cm[j, k] = (
  341. mixes_shared[j * hc_mult + k + hc_mult * 2] * hc_scale[2]
  342. + hc_base[j * hc_mult + k + hc_mult * 2]
  343. )
  344. row_sum = T.alloc_fragment(hc_mult, T.float32)
  345. col_sum = T.alloc_fragment(hc_mult, T.float32)
  346. row_max = T.alloc_fragment(hc_mult, T.float32)
  347. T.reduce_max(cm, row_max, dim=1)
  348. for j, k in T.Parallel(hc_mult, hc_mult):
  349. cm[j, k] = T.exp(cm[j, k] - row_max[j])
  350. T.reduce_sum(cm, row_sum, dim=1)
  351. for j, k in T.Parallel(hc_mult, hc_mult):
  352. cm[j, k] = cm[j, k] / row_sum[j] + hc_sinkhorn_eps
  353. T.reduce_sum(cm, col_sum, dim=0)
  354. for j, k in T.Parallel(hc_mult, hc_mult):
  355. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  356. for _ in T.serial(sinkhorn_repeat - 1):
  357. T.reduce_sum(cm, row_sum, dim=1)
  358. for j, k in T.Parallel(hc_mult, hc_mult):
  359. cm[j, k] = cm[j, k] / (row_sum[j] + hc_sinkhorn_eps)
  360. T.reduce_sum(cm, col_sum, dim=0)
  361. for j, k in T.Parallel(hc_mult, hc_mult):
  362. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  363. for j, k in T.Parallel(hc_mult, hc_mult):
  364. comb_mix[i, j * hc_mult + k] = cm[j, k]
  365. elif split_mode != "stats" and _is_collapse_thread():
  366. pre_mix_shared = T.alloc_shared(hc_mult, T.float32)
  367. for j in T.Parallel(hc_mult):
  368. if use_pre_mix_in:
  369. pre_mix_shared[j] = pre_mix_in[i, j]
  370. elif save_pre_mix:
  371. pre_mix_shared[j] = T.if_then_else(j == 0, 1.0, 0.0)
  372. else:
  373. pre_mix_shared[j] = (
  374. T.sigmoid(mixes_shared[j] * hc_scale[0] + hc_base[j])
  375. + hc_pre_eps
  376. )
  377. # Pass 1: stash unnormalized weighted-sum output in shared memory
  378. # as bf16 (matches the rounding that RMSNorm would see) while
  379. # accumulating the per-position squared sum.
  380. output_shared = T.alloc_shared(hidden_size, T.bfloat16)
  381. sumsq_per_pos = T.alloc_fragment(hidden_block, T.float32)
  382. T.clear(sumsq_per_pos)
  383. for i0_h in T.Pipelined(hidden_size // hidden_block, num_stages=2):
  384. xs = T.alloc_shared((hc_mult, hidden_block), T.bfloat16)
  385. xl = T.alloc_fragment((hc_mult, hidden_block), T.float32)
  386. T.copy(residual[i, 0, i0_h * hidden_block], xs)
  387. T.copy(xs, xl)
  388. ol = T.alloc_fragment(hidden_block, T.float32)
  389. T.clear(ol)
  390. if write_aux:
  391. # Aux consumers (draft models) take the plain stream mean
  392. # of the same residual this collapse reads.
  393. aux = T.alloc_fragment(hidden_block, T.float32)
  394. T.clear(aux)
  395. for i_hc in T.serial(hc_mult):
  396. pre = pre_mix_shared[i_hc]
  397. for i1_h in T.Parallel(hidden_block):
  398. ol[i1_h] += pre * xl[i_hc, i1_h]
  399. if write_aux:
  400. aux[i1_h] += xl[i_hc, i1_h]
  401. if write_aux:
  402. for i1_h in T.Parallel(hidden_block):
  403. aux_out[i, i0_h * hidden_block + i1_h] = T.bfloat16(
  404. aux[i1_h] / hc_mult
  405. )
  406. if save_pre_mix:
  407. # Keep the BF16 boundary before the delayed input RMSNorm.
  408. rounded = T.alloc_fragment(hidden_block, T.bfloat16)
  409. T.copy(ol, rounded)
  410. for i1_h in T.Parallel(hidden_block):
  411. value = T.float32(rounded[i1_h])
  412. sumsq_per_pos[i1_h] += value * value
  413. output_shared[i0_h * hidden_block + i1_h] = rounded[i1_h]
  414. else:
  415. for i1_h in T.Parallel(hidden_block):
  416. sumsq_per_pos[i1_h] += ol[i1_h] * ol[i1_h]
  417. output_shared[i0_h * hidden_block + i1_h] = T.bfloat16(ol[i1_h])
  418. sumsq = T.alloc_fragment(1, T.float32)
  419. T.reduce_sum(sumsq_per_pos, sumsq, dim=0)
  420. rsqrt_norm = T.alloc_fragment(1, T.float32)
  421. rsqrt_norm[0] = T.rsqrt(sumsq[0] / hidden_size + norm_eps)
  422. # Pass 2: scale by rsqrt * norm_weight and write the result to HBM.
  423. for i0_h in T.Pipelined(hidden_size // hidden_block, num_stages=2):
  424. w_shared = T.alloc_shared(hidden_block, T.bfloat16)
  425. w_local = T.alloc_fragment(hidden_block, T.float32)
  426. T.copy(norm_weight[i0_h * hidden_block], w_shared)
  427. T.copy(w_shared, w_local)
  428. ol = T.alloc_fragment(hidden_block, T.float32)
  429. for i1_h in T.Parallel(hidden_block):
  430. ol[i1_h] = (
  431. output_shared[i0_h * hidden_block + i1_h]
  432. * rsqrt_norm[0]
  433. * w_local[i1_h]
  434. )
  435. T.copy(ol, layer_input[i, i0_h * hidden_block])
  436. if ENABLE_PDL:
  437. T.pdl_trigger()
  438. @tilelang_jit
  439. def mhc_pre_big_fuse_broadcast_with_norm_tilelang(
  440. gemm_out_mul,
  441. gemm_out_sqrsum,
  442. hc_scale,
  443. hc_base,
  444. residual,
  445. residual_out,
  446. post_mix,
  447. comb_mix,
  448. layer_input,
  449. norm_weight,
  450. hidden_size: int,
  451. rms_eps: float,
  452. hc_pre_eps: float,
  453. hc_sinkhorn_eps: float,
  454. hc_post_mult_value: float,
  455. sinkhorn_repeat: int,
  456. norm_eps: float,
  457. n_splits: int = 1,
  458. hc_mult: int = 4,
  459. gemm_last_dim: int = -1,
  460. ):
  461. num_tokens = T.dynamic("num_tokens")
  462. hc_mult3 = hc_mult * (2 + hc_mult)
  463. if gemm_last_dim < 0:
  464. gemm_last_dim = hc_mult3
  465. hidden_block = math.gcd(1024, hidden_size)
  466. gemm_out_mul: T.Tensor[[n_splits, num_tokens, gemm_last_dim], T.float32] # type: ignore[no-redef, valid-type]
  467. gemm_out_sqrsum: T.Tensor[[n_splits, num_tokens], T.float32] # type: ignore[no-redef, valid-type]
  468. hc_scale: T.Tensor[[3], T.float32] # type: ignore[no-redef, valid-type]
  469. hc_base: T.Tensor[[hc_mult3], T.float32] # type: ignore[no-redef, valid-type]
  470. residual: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  471. residual_out: T.Tensor[[num_tokens, hc_mult, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  472. post_mix: T.Tensor[[num_tokens, hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  473. comb_mix: T.Tensor[[num_tokens, hc_mult * hc_mult], T.float32] # type: ignore[no-redef, valid-type]
  474. layer_input: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  475. norm_weight: T.Tensor[[hidden_size], T.bfloat16] # type: ignore[no-redef, valid-type]
  476. with T.Kernel(num_tokens, threads=96) as i:
  477. rms = T.alloc_fragment(1, T.float32)
  478. mixes = T.alloc_fragment(hc_mult3, T.float32)
  479. T.clear(mixes)
  480. rms[0] = 0
  481. if ENABLE_PDL:
  482. T.pdl_sync()
  483. for i_split in T.serial(n_splits):
  484. rms[0] += gemm_out_sqrsum[i_split, i]
  485. # rms scale
  486. rms[0] = T.rsqrt(rms[0] / hidden_size + rms_eps)
  487. for j in T.Parallel(hc_mult3):
  488. mixes[j] = 0
  489. for i_split in T.serial(n_splits):
  490. mixes[j] += gemm_out_mul[i_split, i, j]
  491. mixes[j] *= rms[0]
  492. mixes_shared = T.alloc_shared(hc_mult3, T.float32)
  493. T.copy(mixes, mixes_shared)
  494. if _is_mix_thread():
  495. cm = T.alloc_fragment((hc_mult, hc_mult), T.float32)
  496. for j in T.Parallel(hc_mult):
  497. post_mix[i, j] = (
  498. T.sigmoid(
  499. mixes_shared[j + hc_mult] * hc_scale[1] + hc_base[j + hc_mult]
  500. )
  501. * hc_post_mult_value
  502. )
  503. for j, k in T.Parallel(hc_mult, hc_mult):
  504. cm[j, k] = (
  505. mixes_shared[j * hc_mult + k + hc_mult * 2] * hc_scale[2]
  506. + hc_base[j * hc_mult + k + hc_mult * 2]
  507. )
  508. row_sum = T.alloc_fragment(hc_mult, T.float32)
  509. col_sum = T.alloc_fragment(hc_mult, T.float32)
  510. row_max = T.alloc_fragment(hc_mult, T.float32)
  511. T.reduce_max(cm, row_max, dim=1)
  512. for j, k in T.Parallel(hc_mult, hc_mult):
  513. cm[j, k] = T.exp(cm[j, k] - row_max[j])
  514. T.reduce_sum(cm, row_sum, dim=1)
  515. for j, k in T.Parallel(hc_mult, hc_mult):
  516. cm[j, k] = cm[j, k] / row_sum[j] + hc_sinkhorn_eps
  517. T.reduce_sum(cm, col_sum, dim=0)
  518. for j, k in T.Parallel(hc_mult, hc_mult):
  519. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  520. for _ in T.serial(sinkhorn_repeat - 1):
  521. T.reduce_sum(cm, row_sum, dim=1)
  522. for j, k in T.Parallel(hc_mult, hc_mult):
  523. cm[j, k] = cm[j, k] / (row_sum[j] + hc_sinkhorn_eps)
  524. T.reduce_sum(cm, col_sum, dim=0)
  525. for j, k in T.Parallel(hc_mult, hc_mult):
  526. cm[j, k] = cm[j, k] / (col_sum[k] + hc_sinkhorn_eps)
  527. for j, k in T.Parallel(hc_mult, hc_mult):
  528. comb_mix[i, j * hc_mult + k] = cm[j, k]
  529. else:
  530. pre_mix_shared = T.alloc_shared(hc_mult, T.float32)
  531. for j in T.Parallel(hc_mult):
  532. pre_mix_shared[j] = (
  533. T.sigmoid(
  534. mixes_shared[j] * hc_scale[0] + hc_base[j],
  535. )
  536. + hc_pre_eps
  537. )
  538. output_shared = T.alloc_shared(hidden_size, T.bfloat16)
  539. sumsq_per_pos = T.alloc_fragment(hidden_block, T.float32)
  540. T.clear(sumsq_per_pos)
  541. for i0_h in T.Pipelined(hidden_size // hidden_block, num_stages=2):
  542. xs = T.alloc_shared(hidden_block, T.bfloat16)
  543. xl = T.alloc_fragment(hidden_block, T.float32)
  544. T.copy(residual[i, i0_h * hidden_block], xs)
  545. T.copy(xs, xl)
  546. for i_hc, i1_h in T.Parallel(hc_mult, hidden_block):
  547. residual_out[i, i_hc, i0_h * hidden_block + i1_h] = xs[i1_h]
  548. ol = T.alloc_fragment(hidden_block, T.float32)
  549. T.clear(ol)
  550. for i_hc in T.serial(hc_mult):
  551. pre = pre_mix_shared[i_hc]
  552. for i1_h in T.Parallel(hidden_block):
  553. ol[i1_h] += pre * xl[i1_h]
  554. for i1_h in T.Parallel(hidden_block):
  555. sumsq_per_pos[i1_h] += ol[i1_h] * ol[i1_h]
  556. output_shared[i0_h * hidden_block + i1_h] = T.bfloat16(ol[i1_h])
  557. sumsq = T.alloc_fragment(1, T.float32)
  558. T.reduce_sum(sumsq_per_pos, sumsq, dim=0)
  559. rsqrt_norm = T.alloc_fragment(1, T.float32)
  560. rsqrt_norm[0] = T.rsqrt(sumsq[0] / hidden_size + norm_eps)
  561. for i0_h in T.Pipelined(hidden_size // hidden_block, num_stages=2):
  562. w_shared = T.alloc_shared(hidden_block, T.bfloat16)
  563. w_local = T.alloc_fragment(hidden_block, T.float32)
  564. T.copy(norm_weight[i0_h * hidden_block], w_shared)
  565. T.copy(w_shared, w_local)
  566. ol = T.alloc_fragment(hidden_block, T.float32)
  567. for i1_h in T.Parallel(hidden_block):
  568. ol[i1_h] = (
  569. output_shared[i0_h * hidden_block + i1_h]
  570. * rsqrt_norm[0]
  571. * w_local[i1_h]
  572. )
  573. T.copy(ol, layer_input[i, i0_h * hidden_block])
  574. if ENABLE_PDL:
  575. T.pdl_trigger()
  576. @tilelang_jit
  577. def mhc_fused_tilelang(
  578. comb_mix,
  579. residual_in,
  580. post_mix,
  581. x_in,
  582. weight_t,
  583. yp_out,
  584. rp_out,
  585. residual_out,
  586. hc: int,
  587. hidden: int,
  588. n_out: int,
  589. n_thr: int = 256,
  590. h_blk: int = 256,
  591. tile_n: int = 1,
  592. split_k: int = 1,
  593. ) -> tilelang.JITKernel:
  594. """Fused mhc post-mapping + pre-norm GEMM FMA."""
  595. m = T.dynamic("num_tokens")
  596. split_k = T.dynamic("split_k")
  597. h = hidden
  598. h_blk = math.gcd(hidden, h_blk)
  599. h_per_split = h // split_k
  600. n_tiles = n_out // tile_n
  601. comb_mix: T.Tensor((m, hc, hc), T.float32) # type: ignore[no-redef, valid-type]
  602. residual_in: T.Tensor((m, hc, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  603. post_mix: T.Tensor((m, hc), T.float32) # type: ignore[no-redef, valid-type]
  604. x_in: T.Tensor((m, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  605. weight_t: T.Tensor((n_out, hc, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  606. yp_out: T.Tensor((split_k, m, n_out), T.float32) # type: ignore[no-redef, valid-type]
  607. rp_out: T.Tensor((split_k, m), T.float32) # type: ignore[no-redef, valid-type]
  608. residual_out: T.Tensor((m, hc, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  609. h_iters = h_per_split // n_thr
  610. num_warps = n_thr // 32
  611. with T.Kernel(m, n_tiles, split_k, threads=n_thr) as (i_n, i_nt, i_ks):
  612. tid = T.get_thread_binding()
  613. warp_id = tid // 32
  614. lane = tid % 32
  615. s_warp = T.alloc_shared((num_warps, tile_n + 1), T.float32)
  616. s_post = T.alloc_shared((hc,), T.float32)
  617. s_comb = T.alloc_shared((hc, hc), T.float32)
  618. pm = T.alloc_local((hc,), T.float32)
  619. cm = T.alloc_local((hc, hc), T.float32)
  620. acc = T.alloc_local((tile_n,), T.float32)
  621. sqr = T.alloc_local((1,), T.float32)
  622. new_r = T.alloc_local((hc,), T.float32)
  623. T.clear(acc)
  624. T.clear(sqr)
  625. h_split_start = i_ks * h_per_split
  626. if ENABLE_PDL:
  627. T.pdl_sync()
  628. T.copy(post_mix[i_n, 0], s_post)
  629. T.copy(comb_mix[i_n, 0, 0], s_comb)
  630. for j in T.unroll(hc):
  631. pm[j] = s_post[j]
  632. for j in T.unroll(hc):
  633. for k in T.unroll(hc):
  634. cm[k, j] = s_comb[k, j]
  635. # Each thread owns h_iters elements of the k-split's h slice.
  636. for it in T.serial(h_iters):
  637. h_idx = h_split_start + it * n_thr + tid
  638. # Compute new residual from layer output and past residual
  639. for j in T.unroll(hc):
  640. new_r[j] = pm[j] * x_in[i_n, h_idx]
  641. for k in T.unroll(hc):
  642. new_r[j] += cm[k, j] * residual_in[i_n, k, h_idx]
  643. # populate residual_out and compute sqr sum
  644. if i_nt == 0:
  645. for j in T.unroll(hc):
  646. residual_out[i_n, j, h_idx] = new_r[j]
  647. sqr[0] += new_r[j] * new_r[j]
  648. # Per-thread FMA into acc[n]
  649. for n in T.unroll(tile_n):
  650. for j in T.unroll(hc):
  651. acc[n] += T.cast(weight_t[i_nt * tile_n + n, j, h_idx], T.float32) * new_r[j]
  652. for n in T.unroll(tile_n):
  653. acc[n] = T.warp_reduce_sum(acc[n])
  654. if i_nt == 0:
  655. sqr[0] = T.warp_reduce_sum(sqr[0])
  656. # Cross-warp reduce via shared mem
  657. if lane == 0:
  658. for n in T.unroll(tile_n):
  659. s_warp[warp_id, n] = acc[n]
  660. if i_nt == 0:
  661. s_warp[warp_id, tile_n] = sqr[0]
  662. T.sync_threads()
  663. # Warp 0 does the final cross-warp sum and writes outputs
  664. if warp_id == 0:
  665. if lane < tile_n:
  666. v = T.alloc_var(T.float32, init=0.0)
  667. for w in T.unroll(num_warps):
  668. v += s_warp[w, lane]
  669. yp_out[i_ks, i_n, i_nt * tile_n + lane] = v
  670. if i_nt == 0 and lane == 0:
  671. v2 = T.alloc_var(T.float32, init=0.0)
  672. for w in T.unroll(num_warps):
  673. v2 += s_warp[w, tile_n]
  674. rp_out[i_ks, i_n] = v2
  675. if ENABLE_PDL:
  676. T.pdl_trigger()
  677. @tilelang_jit
  678. def mhc_post_tilelang(
  679. a,
  680. b,
  681. c,
  682. d,
  683. x,
  684. hc: int,
  685. hidden: int,
  686. n_thr: int = 128,
  687. h_blk: int = 1024,
  688. ) -> tilelang.JITKernel:
  689. # rename for shorter code
  690. n = T.dynamic("num_tokens")
  691. h = hidden
  692. h_blk = math.gcd(hidden, h_blk)
  693. a: T.Tensor((n, hc, hc), T.float32) # type: ignore[no-redef, valid-type]
  694. b: T.Tensor((n, hc, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  695. c: T.Tensor((n, hc), T.float32) # type: ignore[no-redef, valid-type]
  696. d: T.Tensor((n, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  697. x: T.Tensor((n, hc, h), T.bfloat16) # type: ignore[no-redef, valid-type]
  698. with T.Kernel(n, threads=n_thr) as i_n:
  699. b_shared = T.alloc_shared((hc, h_blk), T.bfloat16)
  700. d_shared = T.alloc_shared(h_blk, T.bfloat16)
  701. x_local = T.alloc_fragment((hc, h_blk), T.float32)
  702. b_local = T.alloc_fragment((hc, h_blk), T.float32)
  703. d_local = T.alloc_fragment(h_blk, T.float32)
  704. a_local = T.alloc_fragment((hc, hc), T.float32)
  705. c_local = T.alloc_fragment(hc, T.float32)
  706. if ENABLE_PDL:
  707. T.pdl_sync()
  708. T.copy(a[i_n, 0, 0], a_local)
  709. T.copy(c[i_n, 0], c_local)
  710. for i0_h in T.Serial(T.ceildiv(h, h_blk)):
  711. T.copy(b[i_n, 0, i0_h * h_blk], b_shared)
  712. T.copy(d[i_n, i0_h * h_blk], d_shared)
  713. T.copy(b_shared, b_local)
  714. T.copy(d_shared, d_local)
  715. for i_hco, i1_h in T.Parallel(hc, h_blk):
  716. x_local[i_hco, i1_h] = c_local[i_hco] * d_local[i1_h]
  717. for i_hci in T.vectorized(hc):
  718. x_local[i_hco, i1_h] += a_local[i_hci, i_hco] * b_local[i_hci, i1_h]
  719. T.copy(x_local, x[i_n, 0, i0_h * h_blk])
  720. if ENABLE_PDL:
  721. T.pdl_trigger()
  722. @tilelang_jit
  723. def hc_prenorm_gemm_tilelang(
  724. x,
  725. fn,
  726. out,
  727. sqrsum,
  728. hidden_size: int,
  729. hc_mult: int = 4,
  730. n_out: int = 24,
  731. n_thr: int = 512,
  732. tile_n: int = 12,
  733. n_splits: int = 1,
  734. ) -> tilelang.JITKernel:
  735. num_tokens = T.dynamic("num_tokens")
  736. hc_hidden_size = hc_mult * hidden_size
  737. k_per_split = hc_hidden_size // n_splits
  738. k_iters = k_per_split // n_thr
  739. n_tiles = T.ceildiv(n_out, tile_n)
  740. x: T.Tensor((num_tokens, hc_hidden_size), T.bfloat16) # type: ignore[no-redef, valid-type]
  741. fn: T.Tensor((n_out, hc_hidden_size), T.float32) # type: ignore[no-redef, valid-type]
  742. out: T.Tensor((n_splits, num_tokens, n_out), T.float32) # type: ignore[no-redef, valid-type]
  743. sqrsum: T.Tensor((n_splits, num_tokens), T.float32) # type: ignore[no-redef, valid-type]
  744. with T.Kernel(num_tokens, n_tiles, n_splits, threads=n_thr) as (
  745. i_n,
  746. i_t,
  747. i_s,
  748. ):
  749. tid = T.get_thread_binding()
  750. acc = T.alloc_local((tile_n,), T.float32)
  751. sqr = T.alloc_local((1,), T.float32)
  752. T.clear(acc)
  753. T.clear(sqr)
  754. if ENABLE_PDL:
  755. T.pdl_sync()
  756. for it in T.serial(k_iters):
  757. i_k = i_s * k_per_split + it * n_thr + tid
  758. x_val = T.cast(x[i_n, i_k], T.float32)
  759. for i_o in T.unroll(tile_n):
  760. out_idx = i_t * tile_n + i_o
  761. if out_idx < n_out:
  762. acc[i_o] += x_val * fn[out_idx, i_k]
  763. if i_t == 0:
  764. sqr[0] += x_val * x_val
  765. for i_o in T.unroll(tile_n):
  766. acc[i_o] = T.warp_reduce_sum(acc[i_o])
  767. if i_t == 0:
  768. sqr[0] = T.warp_reduce_sum(sqr[0])
  769. lane = tid % 32
  770. warp_id = tid // 32
  771. num_warps = n_thr // 32
  772. warp_acc = T.alloc_shared((num_warps, tile_n), T.float32)
  773. warp_sqr = T.alloc_shared(num_warps, T.float32)
  774. if lane == 0:
  775. for i_o in T.unroll(tile_n):
  776. warp_acc[warp_id, i_o] = acc[i_o]
  777. if i_t == 0:
  778. warp_sqr[warp_id] = sqr[0]
  779. T.sync_threads()
  780. if warp_id == 0:
  781. if lane < tile_n:
  782. reduced_acc = T.alloc_var(T.float32, init=0.0)
  783. for i_w in T.unroll(num_warps):
  784. reduced_acc += warp_acc[i_w, lane]
  785. out_idx = i_t * tile_n + lane
  786. if out_idx < n_out:
  787. out[i_s, i_n, out_idx] = reduced_acc
  788. if lane == 0 and i_t == 0:
  789. reduced_sqr = T.alloc_var(T.float32, init=0.0)
  790. for i_w in T.unroll(num_warps):
  791. reduced_sqr += warp_sqr[i_w]
  792. sqrsum[i_s, i_n] = reduced_sqr
  793. if ENABLE_PDL:
  794. T.pdl_trigger()
  795. @tilelang_jit
  796. def hc_prenorm_gemm_block_m_tilelang(
  797. x,
  798. fn,
  799. out,
  800. sqrsum,
  801. hidden_size: int,
  802. hc_mult: int = 4,
  803. n_out: int = 24,
  804. n_thr: int = 512,
  805. tile_n: int = 12,
  806. block_m: int = 2,
  807. ) -> tilelang.JITKernel:
  808. num_tokens = T.dynamic("num_tokens")
  809. hc_hidden_size = hc_mult * hidden_size
  810. k_iters = hc_hidden_size // n_thr
  811. n_tiles = T.ceildiv(n_out, tile_n)
  812. m_tiles = T.ceildiv(num_tokens, block_m)
  813. x: T.Tensor((num_tokens, hc_hidden_size), T.bfloat16) # type: ignore[no-redef, valid-type]
  814. fn: T.Tensor((n_out, hc_hidden_size), T.float32) # type: ignore[no-redef, valid-type]
  815. out: T.Tensor((1, num_tokens, n_out), T.float32) # type: ignore[no-redef, valid-type]
  816. sqrsum: T.Tensor((1, num_tokens), T.float32) # type: ignore[no-redef, valid-type]
  817. with T.Kernel(m_tiles, n_tiles, threads=n_thr) as (i_mt, i_t):
  818. tid = T.get_thread_binding()
  819. acc = T.alloc_local((block_m, tile_n), T.float32)
  820. sqr = T.alloc_local((block_m,), T.float32)
  821. T.clear(acc)
  822. T.clear(sqr)
  823. if ENABLE_PDL:
  824. T.pdl_sync()
  825. for it in T.serial(k_iters):
  826. i_k = it * n_thr + tid
  827. fn_val = T.alloc_local((tile_n,), T.float32)
  828. for i_o in T.unroll(tile_n):
  829. out_idx = i_t * tile_n + i_o
  830. if out_idx < n_out:
  831. fn_val[i_o] = fn[out_idx, i_k]
  832. else:
  833. fn_val[i_o] = 0.0
  834. for i_m in T.unroll(block_m):
  835. token_idx = i_mt * block_m + i_m
  836. if token_idx < num_tokens:
  837. x_val = T.cast(x[token_idx, i_k], T.float32)
  838. for i_o in T.unroll(tile_n):
  839. acc[i_m, i_o] += x_val * fn_val[i_o]
  840. if i_t == 0:
  841. sqr[i_m] += x_val * x_val
  842. for i_m in T.unroll(block_m):
  843. for i_o in T.unroll(tile_n):
  844. acc[i_m, i_o] = T.warp_reduce_sum(acc[i_m, i_o])
  845. if i_t == 0:
  846. sqr[i_m] = T.warp_reduce_sum(sqr[i_m])
  847. lane = tid % 32
  848. warp_id = tid // 32
  849. num_warps = n_thr // 32
  850. warp_acc = T.alloc_shared((num_warps, block_m, tile_n), T.float32)
  851. warp_sqr = T.alloc_shared((num_warps, block_m), T.float32)
  852. if lane == 0:
  853. for i_m in T.unroll(block_m):
  854. for i_o in T.unroll(tile_n):
  855. warp_acc[warp_id, i_m, i_o] = acc[i_m, i_o]
  856. if i_t == 0:
  857. warp_sqr[warp_id, i_m] = sqr[i_m]
  858. T.sync_threads()
  859. if warp_id == 0:
  860. reduced_acc = T.alloc_local((block_m,), T.float32)
  861. reduced_sqr = T.alloc_local((block_m,), T.float32)
  862. T.clear(reduced_acc)
  863. T.clear(reduced_sqr)
  864. for i_m in T.unroll(block_m):
  865. token_idx = i_mt * block_m + i_m
  866. if token_idx < num_tokens:
  867. if lane < tile_n:
  868. for i_w in T.unroll(num_warps):
  869. reduced_acc[i_m] += warp_acc[i_w, i_m, lane]
  870. out_idx = i_t * tile_n + lane
  871. if out_idx < n_out:
  872. out[0, token_idx, out_idx] = reduced_acc[i_m]
  873. if lane == 0 and i_t == 0:
  874. for i_w in T.unroll(num_warps):
  875. reduced_sqr[i_m] += warp_sqr[i_w, i_m]
  876. sqrsum[0, token_idx] = reduced_sqr[i_m]
  877. if ENABLE_PDL:
  878. T.pdl_trigger()
  879. @tilelang_jit
  880. def hc_head_fuse_tilelang(
  881. residual,
  882. fn,
  883. hc_scale,
  884. hc_base,
  885. out,
  886. hidden_size: int,
  887. rms_eps: float,
  888. hc_eps: float,
  889. hc_mult: int = 4,
  890. n_thr: int = 128,
  891. h_blk: int = 1024,
  892. ):
  893. """Two-pass fused kernel for hc_head.
  894. Pass 1: accumulate per-token squared sum and hc_mult dot-products
  895. (projections onto fn rows) using cross-thread reducers.
  896. Pass 2: apply sigmoid-gated weighted sum of residual channels to output.
  897. Avoids materialising mixes / rsqrt / pre tensors to global memory.
  898. """
  899. num_tokens = T.dynamic("num_tokens")
  900. hc_dim = hc_mult * hidden_size
  901. h_block = math.gcd(h_blk, hidden_size)
  902. n_h = hidden_size // h_block
  903. residual: T.Tensor[[num_tokens, hc_mult, hidden_size], T.bfloat16] # type: ignore[no-redef,valid-type]
  904. fn: T.Tensor[[hc_mult, hc_dim], T.float32] # type: ignore[no-redef,valid-type]
  905. hc_scale: T.Tensor[[1], T.float32] # type: ignore[no-redef,valid-type]
  906. hc_base: T.Tensor[[hc_mult], T.float32] # type: ignore[no-redef,valid-type]
  907. out: T.Tensor[[num_tokens, hidden_size], T.bfloat16] # type: ignore[no-redef,valid-type]
  908. with T.Kernel(num_tokens, threads=n_thr) as i:
  909. if ENABLE_PDL:
  910. T.pdl_sync()
  911. # ------------------------------------------------------------------
  912. # Pass 1 – for each residual channel m_c and h_block:
  913. # • accumulate squared sum (for RMS norm denominator)
  914. # • accumulate hc_mult dot-products with fn rows
  915. # ------------------------------------------------------------------
  916. sqrsum_r = T.alloc_reducer((1,), T.float32, replication="all")
  917. mixes_r = T.alloc_reducer((hc_mult,), T.float32, replication="all")
  918. T.fill(sqrsum_r, 0.0)
  919. T.fill(mixes_r, 0.0)
  920. for m_c in T.serial(hc_mult):
  921. for i_h in T.serial(n_h):
  922. x_local = T.alloc_fragment(h_block, T.float32)
  923. T.copy(residual[i, m_c, i_h * h_block], x_local)
  924. for k in T.Parallel(h_block):
  925. sqrsum_r[0] += x_local[k] * x_local[k]
  926. for m_m in T.unroll(hc_mult):
  927. fn_local = T.alloc_fragment(h_block, T.float32)
  928. T.copy(fn[m_m, m_c * hidden_size + i_h * h_block], fn_local)
  929. for k in T.Parallel(h_block):
  930. mixes_r[m_m] += x_local[k] * fn_local[k]
  931. T.finalize_reducer(sqrsum_r)
  932. T.finalize_reducer(mixes_r)
  933. # ------------------------------------------------------------------
  934. # Compute pre_mix = sigmoid(mix * rsqrt * scale + base) + eps
  935. # ------------------------------------------------------------------
  936. pre_mix_shared = T.alloc_shared(hc_mult, T.float32)
  937. rsqrt_val = T.alloc_fragment(1, T.float32)
  938. rsqrt_val[0] = T.rsqrt(sqrsum_r[0] / hc_dim + rms_eps)
  939. for m in T.Parallel(hc_mult):
  940. pre_mix_shared[m] = (
  941. T.sigmoid(mixes_r[m] * rsqrt_val[0] * hc_scale[0] + hc_base[m]) + hc_eps
  942. )
  943. # ------------------------------------------------------------------
  944. # Pass 2 – apply_mix: pipelined weighted sum over residual channels
  945. # ------------------------------------------------------------------
  946. for i0_h in T.Pipelined(n_h, num_stages=2):
  947. xs = T.alloc_shared((hc_mult, h_block), T.bfloat16)
  948. xl = T.alloc_fragment((hc_mult, h_block), T.float32)
  949. T.copy(residual[i, 0, i0_h * h_block], xs, disable_tma=True)
  950. T.copy(xs, xl)
  951. ol = T.alloc_fragment(h_block, T.float32)
  952. T.clear(ol)
  953. for i_hc in T.serial(hc_mult):
  954. pre = pre_mix_shared[i_hc]
  955. for i1_h in T.Parallel(h_block):
  956. ol[i1_h] += pre * xl[i_hc, i1_h]
  957. T.copy(ol, out[i, i0_h * h_block], disable_tma=True)
  958. if ENABLE_PDL:
  959. T.pdl_trigger()
  960. class HcPrenormGemmTileLangKernel(
  961. VllmTileLangJitKernel["HcPrenormGemmTileLangKernel.CompileKey"]
  962. ):
  963. @dataclass(frozen=True)
  964. class CompileKey:
  965. hidden_size: int
  966. hc_mult: int
  967. n_out: int
  968. n_thr: int
  969. tile_n: int
  970. n_splits: int
  971. use_block_m: bool
  972. block_m: int
  973. @staticmethod
  974. def kernel(compile_key: CompileKey) -> Any:
  975. if compile_key.use_block_m:
  976. return hc_prenorm_gemm_block_m_tilelang
  977. return hc_prenorm_gemm_tilelang
  978. def dispatch( # type: ignore[override]
  979. self,
  980. *,
  981. num_tokens: int,
  982. hc_hidden_size: int,
  983. n_thr: int = 512,
  984. tile_n: int = 12,
  985. n_splits: int = 1,
  986. **compile_key_fields: int,
  987. ) -> CompileKey:
  988. use_default_config = n_splits == 1 and tile_n == 12 and n_thr == 512
  989. use_block_m = use_default_config and num_tokens >= 1024
  990. use_small_tile = (
  991. use_default_config
  992. and not use_block_m
  993. and num_tokens < 128
  994. and hc_hidden_size % 1024 == 0
  995. )
  996. effective_n_thr = 1024 if use_small_tile else n_thr
  997. effective_tile_n = 4 if use_small_tile else tile_n
  998. return self.CompileKey(
  999. **compile_key_fields,
  1000. n_thr=effective_n_thr,
  1001. tile_n=effective_tile_n,
  1002. n_splits=n_splits,
  1003. use_block_m=use_block_m,
  1004. block_m=2 if use_block_m else 1,
  1005. )
  1006. def get_warmup_keys(
  1007. self,
  1008. vllm_config: Any,
  1009. *,
  1010. hidden_size: int,
  1011. hc_mult: int,
  1012. n_out: int,
  1013. ) -> list[CompileKey]:
  1014. max_tokens = vllm_config.scheduler_config.max_num_batched_tokens
  1015. if max_tokens <= 0:
  1016. return []
  1017. return self._trace_dispatch(self.dispatch)(
  1018. num_tokens=WarmupIntRange(
  1019. 1,
  1020. max_tokens + 1,
  1021. advance=lambda value: (
  1022. 128 if value < 128 else 1024 if value < 1024 else max_tokens + 1
  1023. ),
  1024. ),
  1025. hc_hidden_size=hidden_size * hc_mult,
  1026. hidden_size=hidden_size,
  1027. hc_mult=hc_mult,
  1028. n_out=n_out,
  1029. )
  1030. def warmup_inputs(self, compile_key: CompileKey) -> dict[str, Any]:
  1031. num_tokens = 128
  1032. if compile_key.use_block_m:
  1033. num_tokens = 1024
  1034. elif compile_key.n_thr == 1024:
  1035. num_tokens = 1
  1036. hc_hidden_size = compile_key.hidden_size * compile_key.hc_mult
  1037. return dict(
  1038. x=make_tilelang_warmup_tensor(torch.bfloat16, num_tokens, hc_hidden_size),
  1039. fn=make_tilelang_warmup_tensor(
  1040. torch.float32, compile_key.n_out, hc_hidden_size
  1041. ),
  1042. out=make_tilelang_warmup_tensor(
  1043. torch.float32,
  1044. compile_key.n_splits,
  1045. num_tokens,
  1046. compile_key.n_out,
  1047. ),
  1048. sqrsum=make_tilelang_warmup_tensor(
  1049. torch.float32, compile_key.n_splits, num_tokens
  1050. ),
  1051. hidden_size=compile_key.hidden_size,
  1052. hc_mult=compile_key.hc_mult,
  1053. tile_n=compile_key.tile_n,
  1054. n_thr=compile_key.n_thr,
  1055. n_splits=compile_key.n_splits,
  1056. )
  1057. @kernel_launcher
  1058. def __call__(
  1059. self,
  1060. x: Any,
  1061. fn: Any,
  1062. out: Any,
  1063. sqrsum: Any,
  1064. hidden_size: int,
  1065. hc_mult: int,
  1066. tile_n: int = 12,
  1067. n_thr: int = 512,
  1068. n_splits: int = 1,
  1069. ) -> TileLangLaunchSpec:
  1070. assert out.shape[0] == n_splits
  1071. assert sqrsum.shape[0] == n_splits
  1072. assert x.shape[1] == hc_mult * hidden_size
  1073. assert x.shape[1] % n_splits == 0
  1074. assert (x.shape[1] // n_splits) % n_thr == 0
  1075. compile_key = self.dispatch(
  1076. num_tokens=x.shape[0],
  1077. hc_hidden_size=x.shape[1],
  1078. hidden_size=hidden_size,
  1079. hc_mult=hc_mult,
  1080. n_out=fn.shape[0],
  1081. n_thr=n_thr,
  1082. tile_n=tile_n,
  1083. n_splits=n_splits,
  1084. )
  1085. kernel_arg = (
  1086. compile_key.block_m if compile_key.use_block_m else compile_key.n_splits
  1087. )
  1088. return (compile_key,), (
  1089. x,
  1090. fn,
  1091. out,
  1092. sqrsum,
  1093. hidden_size,
  1094. hc_mult,
  1095. fn.shape[0],
  1096. compile_key.n_thr,
  1097. compile_key.tile_n,
  1098. kernel_arg,
  1099. )
  1100. class MhcPreBigFuseTileLangKernel(
  1101. VllmTileLangJitKernel["MhcPreBigFuseTileLangKernel.CompileKey"]
  1102. ):
  1103. @dataclass(frozen=True)
  1104. class CompileKey:
  1105. hidden_size: int
  1106. hc_mult: int
  1107. n_splits: int
  1108. use_norm_weight: bool
  1109. is_broadcast: bool
  1110. rms_eps: float
  1111. hc_pre_eps: float
  1112. hc_sinkhorn_eps: float
  1113. hc_post_mult_value: float
  1114. sinkhorn_repeat: int
  1115. norm_eps: float
  1116. @staticmethod
  1117. def kernel(compile_key: CompileKey) -> Any:
  1118. if compile_key.is_broadcast:
  1119. return mhc_pre_big_fuse_broadcast_with_norm_tilelang
  1120. if compile_key.use_norm_weight:
  1121. return mhc_pre_big_fuse_with_norm_tilelang
  1122. return mhc_pre_big_fuse_tilelang
  1123. @staticmethod
  1124. def _kernel_args(
  1125. compile_key: CompileKey,
  1126. *,
  1127. gemm_out_mul: Any,
  1128. gemm_out_sqrsum: Any,
  1129. hc_scale: Any,
  1130. hc_base: Any,
  1131. residual: Any,
  1132. residual_out: Any | None,
  1133. post_mix: Any,
  1134. comb_mix: Any,
  1135. layer_input: Any,
  1136. norm_weight: Any | None,
  1137. ) -> tuple[Any, ...]:
  1138. common_args = (
  1139. gemm_out_mul,
  1140. gemm_out_sqrsum,
  1141. hc_scale,
  1142. hc_base,
  1143. residual,
  1144. )
  1145. output_args = (post_mix, comb_mix, layer_input)
  1146. eps_args = (
  1147. compile_key.rms_eps,
  1148. compile_key.hc_pre_eps,
  1149. compile_key.hc_sinkhorn_eps,
  1150. compile_key.hc_post_mult_value,
  1151. compile_key.sinkhorn_repeat,
  1152. )
  1153. middle_args: tuple[Any, ...]
  1154. if compile_key.is_broadcast:
  1155. assert residual_out is not None
  1156. assert norm_weight is not None
  1157. middle_args = (residual_out, *output_args, norm_weight)
  1158. elif compile_key.use_norm_weight:
  1159. assert norm_weight is not None
  1160. # layer_input stands in for the aux buffer on both epilogues: same
  1161. # shape and dtype, and write_aux is off here so it is never written.
  1162. middle_args = (
  1163. *output_args,
  1164. norm_weight,
  1165. post_mix,
  1166. post_mix,
  1167. layer_input,
  1168. )
  1169. else:
  1170. middle_args = (*output_args, post_mix, post_mix, layer_input)
  1171. norm_eps_args = (compile_key.norm_eps,) if compile_key.use_norm_weight else ()
  1172. return (
  1173. *common_args,
  1174. *middle_args,
  1175. compile_key.hidden_size,
  1176. *eps_args,
  1177. *norm_eps_args,
  1178. compile_key.n_splits,
  1179. compile_key.hc_mult,
  1180. )
  1181. def dispatch( # type: ignore[override]
  1182. self,
  1183. *,
  1184. hidden_size: int,
  1185. hc_mult: int,
  1186. n_splits: int,
  1187. is_broadcast: bool,
  1188. use_norm_weight: bool,
  1189. use_pre_gemm_splits: bool = False,
  1190. pre_gemm_k: int = 0,
  1191. sinkhorn_repeat: int,
  1192. norm_eps: float,
  1193. broadcast_norm_eps: float = 0.0,
  1194. num_tokens: int = 1,
  1195. use_fused_tilelang: bool = False,
  1196. **compile_key_fields: float,
  1197. ) -> CompileKey:
  1198. pre_gemm_n_splits = (
  1199. compute_num_split(64, pre_gemm_k, (num_tokens + 63) // 64)
  1200. if use_pre_gemm_splits
  1201. else n_splits
  1202. )
  1203. # The epilogue reduces over whatever the fused kernel split the GEMM
  1204. # into, so both have to read the same config.
  1205. fused_config = mhc_fused_post_pre_split_config(num_tokens, hidden_size, hc_mult)
  1206. actual_n_splits = (
  1207. fused_config[1]
  1208. if use_fused_tilelang and fused_config is not None
  1209. else pre_gemm_n_splits
  1210. )
  1211. actual_norm_eps = broadcast_norm_eps if is_broadcast else norm_eps
  1212. actual_use_norm_weight = use_norm_weight or is_broadcast
  1213. return self.CompileKey(
  1214. **compile_key_fields,
  1215. hidden_size=hidden_size,
  1216. hc_mult=hc_mult,
  1217. n_splits=actual_n_splits,
  1218. use_norm_weight=actual_use_norm_weight,
  1219. is_broadcast=is_broadcast,
  1220. sinkhorn_repeat=sinkhorn_repeat,
  1221. norm_eps=actual_norm_eps if actual_use_norm_weight else 0.0,
  1222. )
  1223. def get_warmup_keys(
  1224. self,
  1225. vllm_config: Any,
  1226. *,
  1227. hidden_size: int,
  1228. hc_mult: int,
  1229. use_norm_weight: bool,
  1230. rms_eps: float,
  1231. hc_pre_eps: float,
  1232. hc_sinkhorn_eps: float,
  1233. hc_post_mult_value: float,
  1234. sinkhorn_repeat: int,
  1235. norm_eps: float | tuple[float, ...],
  1236. broadcast_norm_eps: float | tuple[float, ...] | None = None,
  1237. include_pre_gemm_splits: bool = False,
  1238. include_broadcast_splits: bool = False,
  1239. ) -> list[CompileKey]:
  1240. max_tokens = vllm_config.scheduler_config.max_num_batched_tokens
  1241. if max_tokens <= 0:
  1242. return []
  1243. warmup_cases = zip_inputs(
  1244. dict(
  1245. is_broadcast=False,
  1246. use_pre_gemm_splits=False,
  1247. pre_gemm_k=0,
  1248. use_fused_tilelang=False,
  1249. ),
  1250. dict(
  1251. is_broadcast=False,
  1252. use_pre_gemm_splits=include_pre_gemm_splits,
  1253. pre_gemm_k=hc_mult * hidden_size,
  1254. use_fused_tilelang=False,
  1255. ),
  1256. dict(
  1257. is_broadcast=include_broadcast_splits,
  1258. use_pre_gemm_splits=include_broadcast_splits,
  1259. pre_gemm_k=hidden_size,
  1260. use_fused_tilelang=False,
  1261. ),
  1262. dict(
  1263. is_broadcast=False,
  1264. use_pre_gemm_splits=False,
  1265. pre_gemm_k=0,
  1266. use_fused_tilelang=True,
  1267. ),
  1268. )
  1269. return self._trace_dispatch(self.dispatch)(
  1270. warmup_cases,
  1271. num_tokens=WarmupIntRange(
  1272. 1,
  1273. max_tokens + 1,
  1274. advance=lambda value: 8 if value < 8 else cdiv(value, 64) * 64 + 1,
  1275. ),
  1276. n_splits=1,
  1277. hidden_size=hidden_size,
  1278. hc_mult=hc_mult,
  1279. use_norm_weight=use_norm_weight,
  1280. rms_eps=rms_eps,
  1281. hc_pre_eps=hc_pre_eps,
  1282. hc_sinkhorn_eps=hc_sinkhorn_eps,
  1283. hc_post_mult_value=hc_post_mult_value,
  1284. sinkhorn_repeat=sinkhorn_repeat,
  1285. norm_eps=norm_eps,
  1286. broadcast_norm_eps=broadcast_norm_eps
  1287. if broadcast_norm_eps is not None
  1288. else norm_eps,
  1289. _when=lambda *, use_fused_tilelang, num_tokens: (
  1290. not use_fused_tilelang or num_tokens <= 16
  1291. ),
  1292. )
  1293. def warmup_inputs(self, compile_key: CompileKey) -> dict[str, Any]:
  1294. hidden_size = compile_key.hidden_size
  1295. hc_mult = compile_key.hc_mult
  1296. hc_mult3 = hc_mult * (2 + hc_mult)
  1297. num_tokens = 1
  1298. gemm_out_mul = make_tilelang_warmup_tensor(
  1299. torch.float32, compile_key.n_splits, num_tokens, hc_mult3
  1300. )
  1301. gemm_out_sqrsum = make_tilelang_warmup_tensor(
  1302. torch.float32, compile_key.n_splits, num_tokens
  1303. )
  1304. hc_scale = make_tilelang_warmup_tensor(torch.float32, 3)
  1305. hc_base = make_tilelang_warmup_tensor(torch.float32, hc_mult3)
  1306. post_mix = make_tilelang_warmup_tensor(torch.float32, num_tokens, hc_mult)
  1307. comb_mix = make_tilelang_warmup_tensor(
  1308. torch.float32, num_tokens, hc_mult * hc_mult
  1309. )
  1310. layer_input = make_tilelang_warmup_tensor(
  1311. torch.bfloat16, num_tokens, hidden_size
  1312. )
  1313. residual_shape = (
  1314. (num_tokens, hidden_size)
  1315. if compile_key.is_broadcast
  1316. else (num_tokens, hc_mult, hidden_size)
  1317. )
  1318. residual = make_tilelang_warmup_tensor(torch.bfloat16, *residual_shape)
  1319. residual_out = (
  1320. make_tilelang_warmup_tensor(
  1321. torch.bfloat16, num_tokens, hc_mult, hidden_size
  1322. )
  1323. if compile_key.is_broadcast
  1324. else None
  1325. )
  1326. norm_weight = (
  1327. make_tilelang_warmup_tensor(torch.bfloat16, hidden_size)
  1328. if compile_key.is_broadcast or compile_key.use_norm_weight
  1329. else None
  1330. )
  1331. return dict(
  1332. gemm_out_mul=gemm_out_mul,
  1333. gemm_out_sqrsum=gemm_out_sqrsum,
  1334. hc_scale=hc_scale,
  1335. hc_base=hc_base,
  1336. residual=residual,
  1337. post_mix=post_mix,
  1338. comb_mix=comb_mix,
  1339. layer_input=layer_input,
  1340. rms_eps=compile_key.rms_eps,
  1341. hc_pre_eps=compile_key.hc_pre_eps,
  1342. hc_sinkhorn_eps=compile_key.hc_sinkhorn_eps,
  1343. hc_post_mult_value=compile_key.hc_post_mult_value,
  1344. sinkhorn_repeat=compile_key.sinkhorn_repeat,
  1345. residual_out=residual_out,
  1346. norm_weight=norm_weight,
  1347. norm_eps=compile_key.norm_eps,
  1348. )
  1349. @kernel_launcher
  1350. def __call__(
  1351. self,
  1352. gemm_out_mul: Any,
  1353. gemm_out_sqrsum: Any,
  1354. hc_scale: Any,
  1355. hc_base: Any,
  1356. residual: Any,
  1357. post_mix: Any,
  1358. comb_mix: Any,
  1359. layer_input: Any,
  1360. rms_eps: float,
  1361. hc_pre_eps: float,
  1362. hc_sinkhorn_eps: float,
  1363. hc_post_mult_value: float,
  1364. sinkhorn_repeat: int,
  1365. *,
  1366. residual_out: Any | None = None,
  1367. norm_weight: Any | None = None,
  1368. norm_eps: float = 0.0,
  1369. ) -> TileLangLaunchSpec:
  1370. is_broadcast = residual_out is not None
  1371. use_norm_weight = norm_weight is not None
  1372. n_splits = gemm_out_mul.shape[0]
  1373. if is_broadcast:
  1374. assert residual_out is not None
  1375. hidden_size = residual.shape[-1]
  1376. hc_mult = residual_out.shape[-2]
  1377. else:
  1378. hc_mult = residual.shape[-2]
  1379. hidden_size = residual.shape[-1]
  1380. compile_key = self.dispatch(
  1381. hidden_size=hidden_size,
  1382. hc_mult=hc_mult,
  1383. n_splits=n_splits,
  1384. is_broadcast=is_broadcast,
  1385. use_norm_weight=use_norm_weight,
  1386. rms_eps=rms_eps,
  1387. hc_pre_eps=hc_pre_eps,
  1388. hc_sinkhorn_eps=hc_sinkhorn_eps,
  1389. hc_post_mult_value=hc_post_mult_value,
  1390. sinkhorn_repeat=sinkhorn_repeat,
  1391. norm_eps=norm_eps,
  1392. broadcast_norm_eps=norm_eps,
  1393. )
  1394. return (
  1395. (compile_key,),
  1396. self._kernel_args(
  1397. compile_key,
  1398. gemm_out_mul=gemm_out_mul,
  1399. gemm_out_sqrsum=gemm_out_sqrsum,
  1400. hc_scale=hc_scale,
  1401. hc_base=hc_base,
  1402. residual=residual,
  1403. residual_out=residual_out,
  1404. post_mix=post_mix,
  1405. comb_mix=comb_mix,
  1406. layer_input=layer_input,
  1407. norm_weight=norm_weight,
  1408. ),
  1409. )
  1410. class MhcPostTileLangKernel(VllmTileLangJitKernel["MhcPostTileLangKernel.CompileKey"]):
  1411. @dataclass(frozen=True)
  1412. class CompileKey:
  1413. hidden_size: int
  1414. hc_mult: int
  1415. @staticmethod
  1416. def kernel() -> Any:
  1417. return mhc_post_tilelang
  1418. def dispatch( # type: ignore[override]
  1419. self,
  1420. **compile_key_fields: int,
  1421. ) -> CompileKey:
  1422. return self.CompileKey(**compile_key_fields)
  1423. def get_warmup_keys(
  1424. self,
  1425. *,
  1426. hidden_size: int,
  1427. hc_mult: int,
  1428. ) -> list[CompileKey]:
  1429. return self._trace_dispatch(self.dispatch)(
  1430. hidden_size=hidden_size,
  1431. hc_mult=hc_mult,
  1432. )
  1433. def warmup_inputs(self, compile_key: CompileKey) -> dict[str, Any]:
  1434. num_tokens = 1
  1435. hidden_size = compile_key.hidden_size
  1436. hc_mult = compile_key.hc_mult
  1437. comb_mix = make_tilelang_warmup_tensor(
  1438. torch.float32, num_tokens, hc_mult, hc_mult
  1439. )
  1440. residual = make_tilelang_warmup_tensor(
  1441. torch.bfloat16, num_tokens, hc_mult, hidden_size
  1442. )
  1443. post_mix = make_tilelang_warmup_tensor(torch.float32, num_tokens, hc_mult)
  1444. layer_input = make_tilelang_warmup_tensor(
  1445. torch.bfloat16, num_tokens, hidden_size
  1446. )
  1447. out = make_tilelang_warmup_tensor(
  1448. torch.bfloat16, num_tokens, hc_mult, hidden_size
  1449. )
  1450. return dict(
  1451. comb_mix=comb_mix,
  1452. residual=residual,
  1453. post_mix=post_mix,
  1454. layer_input=layer_input,
  1455. out=out,
  1456. hc_mult=hc_mult,
  1457. hidden_size=hidden_size,
  1458. )
  1459. @kernel_launcher
  1460. def __call__(
  1461. self,
  1462. comb_mix: Any,
  1463. residual: Any,
  1464. post_mix: Any,
  1465. layer_input: Any,
  1466. out: Any,
  1467. hc_mult: int,
  1468. hidden_size: int,
  1469. ) -> TileLangLaunchSpec:
  1470. return (), (
  1471. comb_mix,
  1472. residual,
  1473. post_mix,
  1474. layer_input,
  1475. out,
  1476. hc_mult,
  1477. hidden_size,
  1478. )
  1479. class MhcFusedTileLangKernel(
  1480. VllmTileLangJitKernel["MhcFusedTileLangKernel.CompileKey"]
  1481. ):
  1482. @dataclass(frozen=True)
  1483. class CompileKey:
  1484. hidden_size: int
  1485. hc_mult: int
  1486. n_splits: int
  1487. tile_n: int
  1488. n_thr: int
  1489. @staticmethod
  1490. def kernel() -> Any:
  1491. return mhc_fused_tilelang
  1492. def dispatch( # type: ignore[override]
  1493. self,
  1494. *,
  1495. num_tokens: int,
  1496. hidden_size: int,
  1497. hc_mult: int,
  1498. ) -> CompileKey:
  1499. # Subscripts rather than an unpack: the warmup tracer parses this body
  1500. # and allows only plain assignments before the return.
  1501. config = require_fused_post_pre_config(num_tokens, hidden_size, hc_mult)
  1502. return self.CompileKey(
  1503. hidden_size=hidden_size,
  1504. hc_mult=hc_mult,
  1505. n_splits=config[1],
  1506. tile_n=config[0],
  1507. n_thr=config[2],
  1508. )
  1509. def get_warmup_keys(
  1510. self,
  1511. vllm_config: Any,
  1512. *,
  1513. hidden_size: int,
  1514. hc_mult: int,
  1515. ) -> list[CompileKey]:
  1516. max_tokens = vllm_config.scheduler_config.max_num_batched_tokens
  1517. return self._trace_dispatch(self.dispatch)(
  1518. num_tokens=WarmupIntRange(1, max_tokens + 1),
  1519. hidden_size=hidden_size,
  1520. hc_mult=hc_mult,
  1521. _when=lambda *, num_tokens: num_tokens <= _FUSED_POST_PRE_MAX_TOKENS,
  1522. )
  1523. def warmup_inputs(self, compile_key: CompileKey) -> dict[str, Any]:
  1524. num_tokens = 1 if compile_key.tile_n == 2 else 16
  1525. hidden_size = compile_key.hidden_size
  1526. hc_mult = compile_key.hc_mult
  1527. hc_mult3 = hc_mult * (2 + hc_mult)
  1528. comb_mix = make_tilelang_warmup_tensor(
  1529. torch.float32, num_tokens, hc_mult, hc_mult
  1530. )
  1531. residual_in = make_tilelang_warmup_tensor(
  1532. torch.bfloat16, num_tokens, hc_mult, hidden_size
  1533. )
  1534. post_mix = make_tilelang_warmup_tensor(torch.float32, num_tokens, hc_mult)
  1535. x_in = make_tilelang_warmup_tensor(torch.bfloat16, num_tokens, hidden_size)
  1536. weight_t = make_tilelang_warmup_tensor(
  1537. torch.bfloat16, hc_mult3, hc_mult, hidden_size
  1538. )
  1539. return dict(
  1540. comb_mix=comb_mix,
  1541. residual_in=residual_in,
  1542. post_mix=post_mix,
  1543. x_in=x_in,
  1544. weight_t=weight_t,
  1545. hc_mult=hc_mult,
  1546. hidden_size=hidden_size,
  1547. hc_mult3=hc_mult3,
  1548. )
  1549. @kernel_launcher
  1550. def __call__(
  1551. self,
  1552. comb_mix: Any,
  1553. residual_in: Any,
  1554. post_mix: Any,
  1555. x_in: Any,
  1556. weight_t: Any,
  1557. hc_mult: int,
  1558. hidden_size: int,
  1559. hc_mult3: int,
  1560. ) -> TileLangLaunchSpec:
  1561. num_tokens = residual_in.shape[0]
  1562. tile_n, n_splits, n_thr = require_fused_post_pre_config(
  1563. num_tokens, hidden_size, hc_mult
  1564. )
  1565. yp_out = residual_in.new_empty(
  1566. (n_splits, num_tokens, hc_mult3), dtype=torch.float32
  1567. )
  1568. rp_out = residual_in.new_empty((n_splits, num_tokens), dtype=torch.float32)
  1569. residual_out = residual_in.new_empty(residual_in.shape)
  1570. return (
  1571. (),
  1572. (
  1573. comb_mix,
  1574. residual_in,
  1575. post_mix,
  1576. x_in,
  1577. weight_t,
  1578. yp_out,
  1579. rp_out,
  1580. residual_out,
  1581. hc_mult,
  1582. hidden_size,
  1583. hc_mult3,
  1584. ),
  1585. dict(n_thr=n_thr, tile_n=tile_n),
  1586. (yp_out, rp_out, residual_out),
  1587. )
  1588. class HcHeadFusedTileLangKernel(
  1589. VllmTileLangJitKernel["HcHeadFusedTileLangKernel.CompileKey"]
  1590. ):
  1591. @dataclass(frozen=True)
  1592. class CompileKey:
  1593. hidden_size: int
  1594. hc_mult: int
  1595. rms_eps: float
  1596. hc_eps: float
  1597. @staticmethod
  1598. def kernel() -> Any:
  1599. return hc_head_fuse_tilelang
  1600. def dispatch( # type: ignore[override]
  1601. self,
  1602. *,
  1603. hidden_size: int,
  1604. hc_mult: int,
  1605. rms_eps: float,
  1606. hc_eps: float,
  1607. ) -> CompileKey:
  1608. return self.CompileKey(
  1609. hidden_size=hidden_size,
  1610. hc_mult=hc_mult,
  1611. rms_eps=rms_eps,
  1612. hc_eps=hc_eps,
  1613. )
  1614. def get_warmup_keys(
  1615. self,
  1616. *,
  1617. hidden_size: int,
  1618. hc_mult: int,
  1619. rms_eps: float,
  1620. hc_eps: float,
  1621. ) -> list[CompileKey]:
  1622. return self._trace_dispatch(self.dispatch)(
  1623. hidden_size=hidden_size,
  1624. hc_mult=hc_mult,
  1625. rms_eps=rms_eps,
  1626. hc_eps=hc_eps,
  1627. )
  1628. def warmup_inputs(self, compile_key: CompileKey) -> dict[str, Any]:
  1629. num_tokens = 1
  1630. hidden_size = compile_key.hidden_size
  1631. hc_mult = compile_key.hc_mult
  1632. residual = make_tilelang_warmup_tensor(
  1633. torch.bfloat16, num_tokens, hc_mult, hidden_size
  1634. )
  1635. fn = make_tilelang_warmup_tensor(torch.float32, hc_mult, hc_mult * hidden_size)
  1636. hc_scale = make_tilelang_warmup_tensor(torch.float32, 1)
  1637. hc_base = make_tilelang_warmup_tensor(torch.float32, hc_mult)
  1638. out = make_tilelang_warmup_tensor(torch.bfloat16, num_tokens, hidden_size)
  1639. return dict(
  1640. residual=residual,
  1641. fn=fn,
  1642. hc_scale=hc_scale,
  1643. hc_base=hc_base,
  1644. out=out,
  1645. hidden_size=hidden_size,
  1646. rms_eps=compile_key.rms_eps,
  1647. hc_eps=compile_key.hc_eps,
  1648. hc_mult=hc_mult,
  1649. )
  1650. @kernel_launcher
  1651. def __call__(
  1652. self,
  1653. residual: Any,
  1654. fn: Any,
  1655. hc_scale: Any,
  1656. hc_base: Any,
  1657. out: Any,
  1658. hidden_size: int,
  1659. rms_eps: float,
  1660. hc_eps: float,
  1661. hc_mult: int,
  1662. ) -> TileLangLaunchSpec:
  1663. return (), (
  1664. residual,
  1665. fn,
  1666. hc_scale,
  1667. hc_base,
  1668. out,
  1669. hidden_size,
  1670. rms_eps,
  1671. hc_eps,
  1672. hc_mult,
  1673. )
  1674. _HC_PRENORM_GEMM_TILELANG_KERNEL = HcPrenormGemmTileLangKernel()
  1675. _MHC_PRE_BIG_FUSE_TILELANG_KERNEL = MhcPreBigFuseTileLangKernel()
  1676. _MHC_POST_TILELANG_KERNEL = MhcPostTileLangKernel()
  1677. _MHC_FUSED_TILELANG_KERNEL = MhcFusedTileLangKernel()
  1678. _HC_HEAD_FUSED_TILELANG_KERNEL = HcHeadFusedTileLangKernel()