compiler.py 93 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843
  1. from __future__ import annotations
  2. import hashlib
  3. import importlib.metadata
  4. import inspect
  5. import json
  6. import math
  7. import os
  8. import re
  9. import shutil
  10. import sys
  11. import tempfile
  12. import time
  13. import traceback
  14. from collections import OrderedDict
  15. from contextlib import contextmanager, suppress
  16. from dataclasses import dataclass, fields, is_dataclass
  17. from functools import lru_cache
  18. from pathlib import Path
  19. from threading import RLock
  20. from types import SimpleNamespace
  21. from typing import Any
  22. from .runtime_patches import apply_cutlass_runtime_patches
  23. apply_cutlass_runtime_patches()
  24. # The package fingerprint hashes every source file under this root; it is
  25. # part of every disk-cache key. Must resolve to b12x.
  26. _PACKAGE_ROOT = Path(__file__).resolve().parents[1]
  27. _MEMORY_CACHE: OrderedDict[object, Any] = OrderedDict()
  28. _MEMORY_CACHE_LOCK = RLock()
  29. _SPEC_MEMO: OrderedDict[tuple[object, ...], Any] = OrderedDict()
  30. _SPEC_MEMO_LOCK = RLock()
  31. _SPEC_MEMO_MAX = 8192
  32. _SPEC_MEMO_HITS = 0
  33. _SPEC_MEMO_MISSES = 0
  34. _MEMORY_CACHE_HITS = 0
  35. _MEMORY_CACHE_MISSES = 0
  36. _DISK_CACHE_HITS = 0
  37. _COMPILE_MISSES = 0
  38. _COMPILE_PROGRESS_LOCK = RLock()
  39. _COMPILE_PROGRESS_COUNT = 0
  40. _COMPILE_PROGRESS_TOTAL_SECONDS = 0.0
  41. _EXECUTOR_CACHE_LOCK = RLock()
  42. _EXECUTOR_CACHE_ATTR = "_b12x_cached_default_executor"
  43. _VLLM_ENGINE_STARTED_ENV = "B12X_ENGINE_STARTED"
  44. _POST_ENGINE_START_LOG_ENV = "B12X_LOG_CUTE_COMPILES_AFTER_ENGINE_START"
  45. _PRINT_COMPILE_PROGRESS_ENV = "B12X_PRINT_COMPILE_PROGRESS"
  46. @dataclass(frozen=True)
  47. class DimKey:
  48. kind: str
  49. value: object = None
  50. @staticmethod
  51. def exact(value: object) -> "DimKey":
  52. return DimKey("exact", value)
  53. @staticmethod
  54. def capacity(value: object) -> "DimKey":
  55. return DimKey("capacity", value)
  56. @staticmethod
  57. def bucket(value: object) -> "DimKey":
  58. return DimKey("bucket", value)
  59. @staticmethod
  60. def dynamic() -> "DimKey":
  61. return DimKey("dynamic")
  62. @staticmethod
  63. def ignored() -> "DimKey":
  64. return DimKey("ignored")
  65. @dataclass(frozen=True)
  66. class TensorKey:
  67. name: str
  68. dtype: str
  69. rank: int
  70. dims: tuple[DimKey, ...]
  71. stride: tuple[int, ...]
  72. device: tuple[str, int | None]
  73. align: int | None = None
  74. layout: object = None
  75. @staticmethod
  76. def from_tensor(
  77. name: str,
  78. tensor: Any,
  79. *,
  80. dims: tuple[DimKey, ...] | None = None,
  81. align: int | None = None,
  82. layout: object = None,
  83. ) -> "TensorKey":
  84. shape = tuple(int(dim) for dim in tensor.shape)
  85. if dims is None:
  86. dims = tuple(DimKey.exact(dim) for dim in shape)
  87. if len(dims) != len(shape):
  88. raise ValueError(
  89. f"tensor key {name!r} dim policy rank {len(dims)} "
  90. f"does not match tensor rank {len(shape)}"
  91. )
  92. device = tensor.device
  93. return TensorKey(
  94. name=name,
  95. dtype=str(tensor.dtype),
  96. rank=len(shape),
  97. dims=dims,
  98. stride=tuple(int(stride) for stride in tensor.stride()),
  99. device=(device.type, device.index),
  100. align=align,
  101. layout=layout,
  102. )
  103. def _spec_memo_enabled() -> bool:
  104. raw = os.environ.get("B12X_COMPILE_SPEC_MEMO", "1")
  105. return raw.lower() not in {"0", "false", "no", ""}
  106. def _spec_memo_key(
  107. kind: str,
  108. kernel_id: str,
  109. version: int,
  110. payload: object,
  111. extra: object = None,
  112. ) -> tuple[object, ...] | None:
  113. # repr() of a JSON-POD payload is content-deterministic and preserves
  114. # type distinctions (True vs 1, tuple vs list), so it is a safe memo key.
  115. # Non-POD payloads never get stored (see _spec_memo_put callers), which
  116. # keeps id-based reprs out of the cache.
  117. if not _spec_memo_enabled():
  118. return None
  119. try:
  120. memo_key = (kind, str(kernel_id), int(version), repr(payload), extra)
  121. hash(memo_key)
  122. except Exception:
  123. return None
  124. return memo_key
  125. def _spec_memo_get(memo_key: tuple[object, ...] | None) -> Any | None:
  126. global _SPEC_MEMO_HITS
  127. global _SPEC_MEMO_MISSES
  128. if memo_key is None:
  129. return None
  130. with _SPEC_MEMO_LOCK:
  131. spec = _SPEC_MEMO.get(memo_key)
  132. if spec is None:
  133. _SPEC_MEMO_MISSES += 1
  134. return None
  135. _SPEC_MEMO_HITS += 1
  136. _SPEC_MEMO.move_to_end(memo_key)
  137. return spec
  138. def _spec_memo_put(memo_key: tuple[object, ...] | None, spec: Any) -> None:
  139. if memo_key is None:
  140. return
  141. with _SPEC_MEMO_LOCK:
  142. _SPEC_MEMO[memo_key] = spec
  143. _SPEC_MEMO.move_to_end(memo_key)
  144. while len(_SPEC_MEMO) > _SPEC_MEMO_MAX:
  145. _SPEC_MEMO.popitem(last=False)
  146. @dataclass(frozen=True)
  147. class KeyField:
  148. name: str
  149. value: object
  150. @dataclass(frozen=True)
  151. class KernelCompileSpec:
  152. kernel_id: str
  153. version: int
  154. fields: tuple[KeyField, ...] = ()
  155. json_key: str = ""
  156. hash_key: str = ""
  157. legacy: bool = False
  158. def __post_init__(self) -> None:
  159. if self.json_key and self.hash_key:
  160. return
  161. json_key = _compile_spec_json(
  162. self.kernel_id,
  163. self.version,
  164. _legacy_compile_spec_facts(self.kernel_id, self.version, self.fields),
  165. )
  166. object.__setattr__(self, "json_key", json_key)
  167. object.__setattr__(self, "hash_key", _hash_json_key(json_key))
  168. object.__setattr__(self, "legacy", True)
  169. @staticmethod
  170. def from_facts(
  171. kernel_id: str,
  172. version: int,
  173. *facts: object,
  174. ) -> "KernelCompileSpec":
  175. memo_key = _spec_memo_key("facts", kernel_id, version, facts)
  176. spec = _spec_memo_get(memo_key)
  177. if spec is not None:
  178. return spec
  179. json_key = _compile_spec_json(kernel_id, version, facts)
  180. spec = KernelCompileSpec(
  181. kernel_id=str(kernel_id),
  182. version=int(version),
  183. fields=(),
  184. json_key=json_key,
  185. hash_key=_hash_json_key(json_key),
  186. legacy=False,
  187. )
  188. # _compile_spec_json raises for non-POD facts, so only POD-validated
  189. # specs reach the memo.
  190. _spec_memo_put(memo_key, spec)
  191. return spec
  192. @staticmethod
  193. def from_fields(
  194. kernel_id: str,
  195. version: int,
  196. *fields: KeyField | tuple[str, object],
  197. ) -> "KernelCompileSpec":
  198. memo_key = _spec_memo_key("fields", kernel_id, version, fields)
  199. spec = _spec_memo_get(memo_key)
  200. if spec is not None:
  201. return spec
  202. coerced_fields = tuple(_coerce_key_field(field) for field in fields)
  203. if all(_is_json_pod(field.value) for field in coerced_fields):
  204. spec = KernelCompileSpec.from_facts(
  205. kernel_id,
  206. version,
  207. *((field.name, field.value) for field in coerced_fields),
  208. )
  209. _spec_memo_put(memo_key, spec)
  210. return spec
  211. return KernelCompileSpec(
  212. kernel_id=kernel_id,
  213. version=int(version),
  214. fields=coerced_fields,
  215. legacy=True,
  216. )
  217. @staticmethod
  218. def from_legacy_fields(
  219. kernel_id: str,
  220. version: int,
  221. *fields: KeyField | tuple[str, object],
  222. ) -> "KernelCompileSpec":
  223. return KernelCompileSpec(
  224. kernel_id=kernel_id,
  225. version=int(version),
  226. fields=tuple(_coerce_key_field(field) for field in fields),
  227. legacy=True,
  228. )
  229. @staticmethod
  230. def from_key(
  231. kernel_id: str,
  232. version: int,
  233. key: tuple[object, ...],
  234. *,
  235. labels: tuple[str, ...] | None = None,
  236. ) -> "KernelCompileSpec":
  237. if labels is not None and len(labels) != len(key):
  238. raise ValueError(
  239. f"compile spec labels length {len(labels)} does not match "
  240. f"key length {len(key)}"
  241. )
  242. memo_key = _spec_memo_key("key", kernel_id, version, key, extra=labels)
  243. spec = _spec_memo_get(memo_key)
  244. if spec is not None:
  245. return spec
  246. if _is_json_pod(key):
  247. facts = (
  248. tuple((str(labels[idx]), value) for idx, value in enumerate(key))
  249. if labels is not None
  250. else key
  251. )
  252. spec = KernelCompileSpec.from_facts(kernel_id, version, *facts)
  253. _spec_memo_put(memo_key, spec)
  254. return spec
  255. return KernelCompileSpec(
  256. kernel_id=kernel_id,
  257. version=int(version),
  258. fields=tuple(
  259. KeyField(labels[idx] if labels is not None else f"arg{idx}", value)
  260. for idx, value in enumerate(key)
  261. ),
  262. legacy=True,
  263. )
  264. @staticmethod
  265. def from_legacy_key(
  266. kernel_id: str,
  267. version: int,
  268. key: tuple[object, ...],
  269. *,
  270. labels: tuple[str, ...] | None = None,
  271. ) -> "KernelCompileSpec":
  272. if labels is not None and len(labels) != len(key):
  273. raise ValueError(
  274. f"compile spec labels length {len(labels)} does not match "
  275. f"key length {len(key)}"
  276. )
  277. return KernelCompileSpec(
  278. kernel_id=kernel_id,
  279. version=int(version),
  280. fields=tuple(
  281. KeyField(labels[idx] if labels is not None else f"arg{idx}", value)
  282. for idx, value in enumerate(key)
  283. ),
  284. legacy=True,
  285. )
  286. def _coerce_key_field(field: KeyField | tuple[str, object]) -> KeyField:
  287. if isinstance(field, KeyField):
  288. return field
  289. name, value = field
  290. return KeyField(str(name), value)
  291. def key_field(name: str, value: object) -> KeyField:
  292. return KeyField(name, value)
  293. def tensor_key(
  294. name: str,
  295. tensor: Any | None,
  296. *,
  297. dims: tuple[DimKey, ...] | None = None,
  298. align: int | None = None,
  299. layout: object = None,
  300. ) -> KeyField:
  301. if tensor is None:
  302. value = None
  303. else:
  304. try:
  305. value = tensor_compile_fact(
  306. name,
  307. tensor,
  308. dims=dims,
  309. align=align,
  310. layout=layout,
  311. )
  312. except TypeError:
  313. value = TensorKey.from_tensor(
  314. name,
  315. tensor,
  316. dims=dims,
  317. align=align,
  318. layout=layout,
  319. )
  320. return KeyField(name, value)
  321. def dim_compile_fact(dim: DimKey | object) -> tuple[str, object]:
  322. return _dim_policy_fact(dim)
  323. def _dim_policy_fact(dim: DimKey | object) -> tuple[str, object]:
  324. if isinstance(dim, DimKey):
  325. return ("dim", str(dim.kind), _json_pod(dim.value, path="dim.value"))
  326. return ("dim", "exact", _json_pod(dim, path="dim.value"))
  327. def tensor_compile_fact(
  328. name: str,
  329. tensor: Any,
  330. *,
  331. dims: tuple[DimKey | object, ...] | None = None,
  332. dynamic_dims: tuple[int, ...] = (),
  333. strides: tuple[DimKey | object, ...] | None = None,
  334. dynamic_strides: tuple[int, ...] = (),
  335. align: int | None = None,
  336. layout: object = None,
  337. ) -> tuple[object, ...]:
  338. shape = tuple(int(dim) for dim in tensor.shape)
  339. if dims is None:
  340. dynamic_dim_set = set(dynamic_dims)
  341. dims = tuple(
  342. DimKey.dynamic() if idx in dynamic_dim_set else DimKey.exact(dim)
  343. for idx, dim in enumerate(shape)
  344. )
  345. if len(dims) != len(shape):
  346. raise ValueError(
  347. f"tensor key {name!r} dim policy rank {len(dims)} "
  348. f"does not match tensor rank {len(shape)}"
  349. )
  350. raw_strides = tuple(int(stride) for stride in tensor.stride())
  351. if strides is None:
  352. dynamic_stride_set = set(dynamic_strides)
  353. strides = tuple(
  354. DimKey.dynamic() if idx in dynamic_stride_set else DimKey.exact(stride)
  355. for idx, stride in enumerate(raw_strides)
  356. )
  357. if len(strides) != len(raw_strides):
  358. raise ValueError(
  359. f"tensor key {name!r} stride policy rank {len(strides)} "
  360. f"does not match tensor rank {len(raw_strides)}"
  361. )
  362. device = tensor.device
  363. layout_fact = None if layout is None else _json_pod(layout, path="layout")
  364. return (
  365. "tensor",
  366. str(name),
  367. str(tensor.dtype),
  368. len(shape),
  369. tuple(_dim_policy_fact(dim) for dim in dims),
  370. tuple(_dim_policy_fact(stride) for stride in strides),
  371. (str(device.type), device.index),
  372. None if align is None else int(align),
  373. layout_fact,
  374. )
  375. def _is_json_scalar(value: Any) -> bool:
  376. if value is None or isinstance(value, (bool, int, str)):
  377. return True
  378. return isinstance(value, float) and math.isfinite(value)
  379. def _is_json_pod(value: Any) -> bool:
  380. if _is_json_scalar(value):
  381. return True
  382. if isinstance(value, DimKey):
  383. return _is_json_pod(value.value)
  384. if isinstance(value, KeyField):
  385. return _is_json_pod(value.value)
  386. if isinstance(value, (TensorKey, KernelCompileSpec)):
  387. return False
  388. if isinstance(value, (tuple, list)):
  389. return all(_is_json_pod(item) for item in value)
  390. if isinstance(value, dict):
  391. return all(
  392. isinstance(key, str) and _is_json_pod(item) for key, item in value.items()
  393. )
  394. return False
  395. def _json_pod(value: Any, *, path: str = "value") -> Any:
  396. if _is_json_scalar(value):
  397. return value
  398. if isinstance(value, float):
  399. raise TypeError(f"{path} must be finite for JSON compile specs")
  400. if isinstance(value, DimKey):
  401. return [
  402. "dim",
  403. str(value.kind),
  404. _json_pod(value.value, path=f"{path}.value"),
  405. ]
  406. if isinstance(value, KeyField):
  407. return [
  408. "field",
  409. str(value.name),
  410. _json_pod(value.value, path=f"{path}.{value.name}"),
  411. ]
  412. if isinstance(value, (TensorKey, KernelCompileSpec)):
  413. raise TypeError(
  414. f"{path} contains legacy compile-key object {type(value).__name__}; "
  415. "use explicit POD facts instead"
  416. )
  417. if isinstance(value, tuple):
  418. return [
  419. _json_pod(item, path=f"{path}[{idx}]") for idx, item in enumerate(value)
  420. ]
  421. if isinstance(value, list):
  422. return [
  423. _json_pod(item, path=f"{path}[{idx}]") for idx, item in enumerate(value)
  424. ]
  425. if isinstance(value, dict):
  426. out = {}
  427. for key, item in value.items():
  428. if not isinstance(key, str):
  429. raise TypeError(f"{path} dict key {key!r} is not a string")
  430. out[key] = _json_pod(item, path=f"{path}.{key}")
  431. return out
  432. raise TypeError(
  433. f"{path} contains non-POD compile fact "
  434. f"{type(value).__module__}.{type(value).__qualname__}"
  435. )
  436. def _json_dumps_pod(value: Any) -> str:
  437. return json.dumps(
  438. _json_pod(value),
  439. sort_keys=True,
  440. separators=(",", ":"),
  441. ensure_ascii=True,
  442. allow_nan=False,
  443. )
  444. def _hash_json_key(json_key: str) -> str:
  445. return hashlib.sha256(json_key.encode("utf-8")).hexdigest()
  446. def _compile_spec_json(
  447. kernel_id: str,
  448. version: int,
  449. facts: object,
  450. ) -> str:
  451. return _json_dumps_pod(
  452. {
  453. "facts": facts,
  454. "kernel": str(kernel_id),
  455. "version": int(version),
  456. }
  457. )
  458. def _legacy_compile_spec_facts(
  459. kernel_id: str,
  460. version: int,
  461. fields: tuple[KeyField, ...],
  462. ) -> tuple[object, ...]:
  463. return (
  464. "legacy",
  465. str(kernel_id),
  466. int(version),
  467. tuple(_compile_spec_shape_key(field) for field in fields),
  468. )
  469. def _compile_kwargs_json_key(kwargs: dict[str, Any]) -> tuple[str, str]:
  470. if not kwargs:
  471. return "", ""
  472. try:
  473. json_key = _json_dumps_pod(kwargs)
  474. except TypeError:
  475. json_key = _json_dumps_pod(_compile_spec_shape_key(kwargs))
  476. return json_key, _hash_json_key(json_key)
  477. def _compile_spec_shape_key(value: Any) -> Any:
  478. if value is None or isinstance(value, (bool, int, float, str)):
  479. return value
  480. if isinstance(value, bytes):
  481. return ("bytes", value)
  482. if isinstance(value, Path):
  483. return ("path", str(value))
  484. if isinstance(value, DimKey):
  485. return ("dim", value.kind, _compile_spec_shape_key(value.value))
  486. if isinstance(value, TensorKey):
  487. return (
  488. "tensor",
  489. value.name,
  490. value.dtype,
  491. value.rank,
  492. tuple(_compile_spec_shape_key(dim) for dim in value.dims),
  493. value.stride,
  494. value.device,
  495. value.align,
  496. _compile_spec_shape_key(value.layout),
  497. )
  498. if isinstance(value, KeyField):
  499. return ("field", value.name, _compile_spec_shape_key(value.value))
  500. if isinstance(value, KernelCompileSpec):
  501. return (
  502. "kernel_spec_json" if not value.legacy else "kernel_spec_legacy_json",
  503. value.kernel_id,
  504. value.version,
  505. value.hash_key,
  506. value.json_key,
  507. )
  508. if isinstance(value, (tuple, list)):
  509. return tuple(_compile_spec_shape_key(item) for item in value)
  510. if isinstance(value, dict):
  511. return (
  512. "dict",
  513. tuple(
  514. (
  515. _compile_spec_shape_key(key),
  516. _compile_spec_shape_key(item_value),
  517. )
  518. for key, item_value in value.items()
  519. ),
  520. )
  521. if isinstance(value, type):
  522. return ("type", value.__module__, value.__qualname__)
  523. cache_key_attr = getattr(value, "__cache_key__", None)
  524. if cache_key_attr is not None:
  525. return (
  526. "cache_key",
  527. type(value).__module__,
  528. type(value).__qualname__,
  529. _compile_spec_shape_key(cache_key_attr),
  530. )
  531. if is_dataclass(value):
  532. return (
  533. "dataclass",
  534. type(value).__module__,
  535. type(value).__qualname__,
  536. tuple(
  537. (
  538. field.name,
  539. _compile_spec_shape_key(getattr(value, field.name)),
  540. )
  541. for field in fields(value)
  542. ),
  543. )
  544. return (
  545. "repr",
  546. type(value).__module__,
  547. type(value).__qualname__,
  548. repr(value),
  549. )
  550. def _compile_memory_cache_key(
  551. compile_callable: Any,
  552. func: Any,
  553. args: tuple[Any, ...],
  554. kwargs: dict[str, Any],
  555. compile_spec: KernelCompileSpec | None,
  556. ) -> object:
  557. if compile_spec is not None:
  558. kwargs_json_key, kwargs_hash_key = _compile_kwargs_json_key(kwargs)
  559. key: tuple[object, ...] = (
  560. "b12x_cute_memory_cache_v2_explicit_spec",
  561. compile_spec.hash_key,
  562. )
  563. if kwargs_hash_key:
  564. key += (kwargs_hash_key, kwargs_json_key)
  565. return key
  566. return hashlib.sha256(
  567. repr(_compile_disk_cache_payload(compile_callable, func, args, kwargs)).encode(
  568. "utf-8"
  569. )
  570. ).hexdigest()
  571. def _cute_compile_memory_cache_enabled() -> bool:
  572. raw = os.environ.get("B12X_COMPILE_MEMORY_CACHE", "1")
  573. return raw.lower() not in {"0", "false", "no", ""}
  574. def _cute_compile_memory_cache_size() -> int:
  575. raw = os.environ.get("B12X_COMPILE_MEMORY_CACHE_SIZE", "1024")
  576. try:
  577. return max(1, int(raw))
  578. except ValueError:
  579. return 1024
  580. def _cute_compile_disk_cache_enabled() -> bool:
  581. raw = os.environ.get("B12X_COMPILE_DISK_CACHE", "1")
  582. return raw.lower() not in {"0", "false", "no", ""}
  583. def _cute_compile_disk_cache_enabled_for_payload(
  584. cache_payload: tuple[object, ...],
  585. ) -> bool:
  586. return (
  587. _cute_compile_disk_cache_enabled()
  588. and len(cache_payload) > 4
  589. and cache_payload[4] is not None
  590. )
  591. def _cute_compile_cache_dir() -> Path:
  592. root = os.environ.get("B12X_COMPILE_CACHE_DIR")
  593. if root:
  594. return Path(root)
  595. xdg_cache_home = os.environ.get("XDG_CACHE_HOME")
  596. if xdg_cache_home:
  597. return Path(xdg_cache_home) / "b12x" / "compile"
  598. return Path.home() / ".cache" / "b12x" / "compile"
  599. def _cute_compile_log_enabled() -> bool:
  600. raw = os.environ.get("B12X_LOG_CUTE_COMPILES", "")
  601. return raw.lower() not in {"", "0", "false", "no", "off"}
  602. def _cute_compile_progress_enabled() -> bool:
  603. raw = os.environ.get(_PRINT_COMPILE_PROGRESS_ENV, "")
  604. return raw.lower() not in {"", "0", "false", "no", "off"}
  605. def _cute_compile_post_engine_start_log_enabled() -> bool:
  606. marker = os.environ.get(_VLLM_ENGINE_STARTED_ENV, "")
  607. if marker.lower() in {"", "0", "false", "no", "off"}:
  608. return False
  609. raw = os.environ.get(_POST_ENGINE_START_LOG_ENV)
  610. if raw is None:
  611. return True
  612. return raw.lower() not in {"", "0", "false", "no", "off"}
  613. def _cute_compile_stack_log_enabled() -> bool:
  614. raw = os.environ.get("B12X_LOG_CUTE_COMPILE_STACK", "")
  615. if raw:
  616. return raw.lower() not in {"0", "false", "no", "off"}
  617. raw = os.environ.get("B12X_LOG_CUTE_COMPILES", "")
  618. if raw.lower() in {"stack", "trace", "traceback", "full"}:
  619. return True
  620. raw = os.environ.get(_POST_ENGINE_START_LOG_ENV, "")
  621. return raw.lower() in {"stack", "trace", "traceback", "full"}
  622. def _cute_compile_stack_log_depth() -> int:
  623. raw = os.environ.get("B12X_LOG_CUTE_COMPILE_STACK_DEPTH", "")
  624. if not raw:
  625. return 48
  626. try:
  627. return max(1, int(raw))
  628. except ValueError:
  629. return 48
  630. def _short_repr(value: Any, *, max_len: int = 160) -> str:
  631. try:
  632. if isinstance(value, type):
  633. text = f"{value.__module__}.{value.__qualname__}"
  634. else:
  635. text = repr(value)
  636. except Exception:
  637. text = f"<{type(value).__module__}.{type(value).__qualname__}>"
  638. if len(text) > max_len:
  639. return text[: max_len - 3] + "..."
  640. return text
  641. def _compile_target_name(func: Any) -> str:
  642. unwrapped = inspect.unwrap(func)
  643. if inspect.ismethod(unwrapped):
  644. module = getattr(unwrapped.__func__, "__module__", "")
  645. qualname = getattr(
  646. unwrapped.__func__,
  647. "__qualname__",
  648. getattr(unwrapped.__func__, "__name__", ""),
  649. )
  650. return f"{module}.{qualname}" if module else qualname
  651. if inspect.isfunction(unwrapped):
  652. module = getattr(unwrapped, "__module__", "")
  653. qualname = getattr(
  654. unwrapped, "__qualname__", getattr(unwrapped, "__name__", "")
  655. )
  656. return f"{module}.{qualname}" if module else qualname
  657. target_type = type(func)
  658. module = getattr(target_type, "__module__", "")
  659. qualname = getattr(target_type, "__qualname__", target_type.__name__)
  660. return f"{module}.{qualname}" if module else qualname
  661. def _simple_log_value(value: Any) -> Any | None:
  662. if value is None or isinstance(value, (bool, int, float, str)):
  663. return value
  664. if isinstance(value, type):
  665. return f"{value.__module__}.{value.__qualname__}"
  666. if isinstance(value, (tuple, list)) and len(value) <= 8:
  667. items = []
  668. for item in value:
  669. simple = _simple_log_value(item)
  670. if simple is None:
  671. return None
  672. items.append(simple)
  673. return tuple(items) if isinstance(value, tuple) else items
  674. return None
  675. def _compile_target_attrs(func: Any) -> dict[str, Any]:
  676. if not hasattr(func, "__dict__"):
  677. return {}
  678. attrs = {}
  679. for name, value in sorted(vars(func).items()):
  680. if name.startswith("_"):
  681. continue
  682. simple = _simple_log_value(value)
  683. if simple is None:
  684. continue
  685. attrs[name] = simple
  686. if len(attrs) >= 48:
  687. attrs["..."] = "truncated"
  688. break
  689. return attrs
  690. def _compile_arg_shape_summary(value: Any) -> dict[str, Any] | None:
  691. shape = _first_present_attr(value, "_shape", "shape")
  692. if shape is None:
  693. return None
  694. try:
  695. shape_value = tuple(shape)
  696. except TypeError:
  697. shape_value = shape
  698. summary: dict[str, Any] = {
  699. "type": f"{type(value).__module__}.{type(value).__qualname__}",
  700. "shape": _short_repr(shape_value, max_len=96),
  701. }
  702. stride = _first_present_attr(value, "_stride", "stride")
  703. if stride is not None:
  704. try:
  705. stride_value = tuple(stride)
  706. except TypeError:
  707. stride_value = stride
  708. summary["stride"] = _short_repr(stride_value, max_len=96)
  709. stride_order = _first_present_attr(value, "_stride_order", "stride_order")
  710. if stride_order is not None:
  711. try:
  712. stride_order_value = tuple(stride_order)
  713. except TypeError:
  714. stride_order_value = stride_order
  715. summary["stride_order"] = _short_repr(stride_order_value, max_len=96)
  716. dtype = _first_present_attr(value, "_dtype", "dtype", "element_type")
  717. if dtype is not None:
  718. summary["dtype"] = _short_repr(dtype, max_len=80)
  719. memspace = _first_present_attr(value, "_memspace", "memspace")
  720. if memspace is not None:
  721. summary["memspace"] = _short_repr(memspace, max_len=80)
  722. assumed_align = _first_present_attr(value, "_assumed_align", "assumed_align")
  723. if assumed_align is not None:
  724. summary["align"] = assumed_align
  725. return summary
  726. def _compile_args_shape_summary(
  727. args: tuple[Any, ...], kwargs: dict[str, Any]
  728. ) -> dict[str, Any]:
  729. summary: dict[str, Any] = {}
  730. for idx, value in enumerate(args):
  731. shaped = _compile_arg_shape_summary(value)
  732. if shaped is not None:
  733. summary[f"arg{idx}"] = shaped
  734. elif value is None or isinstance(value, (bool, int, float, str)):
  735. summary[f"arg{idx}"] = value
  736. if len(summary) >= 24:
  737. summary["..."] = "truncated"
  738. return summary
  739. for name, value in sorted(kwargs.items()):
  740. shaped = _compile_arg_shape_summary(value)
  741. if shaped is not None:
  742. summary[f"kw:{name}"] = shaped
  743. elif value is None or isinstance(value, (bool, int, float, str)):
  744. summary[f"kw:{name}"] = value
  745. if len(summary) >= 24:
  746. summary["..."] = "truncated"
  747. return summary
  748. return summary
  749. def _type_log_name(module: str, qualname: str) -> str:
  750. return f"{module}.{qualname}" if module else qualname
  751. def _function_fingerprint_log_value(value: Any) -> Any:
  752. if isinstance(value, tuple) and len(value) == 3:
  753. module, qualname, _fingerprint = value
  754. if isinstance(module, str) and isinstance(qualname, str):
  755. return _type_log_name(module, qualname)
  756. return _cache_key_log_value(value, max_depth=2, max_items=8)
  757. def _object_state_log_value(
  758. value: Any, *, max_depth: int, max_items: int
  759. ) -> dict[str, Any] | None:
  760. if not (isinstance(value, tuple) and len(value) == 4 and value[0] == "object"):
  761. return None
  762. _tag, module, qualname, attrs = value
  763. if not isinstance(attrs, tuple):
  764. return None
  765. state: dict[str, Any] = {}
  766. for idx, item in enumerate(attrs):
  767. if idx >= max_items:
  768. state["..."] = f"{len(attrs) - idx} more"
  769. break
  770. if not (isinstance(item, tuple) and len(item) == 2):
  771. continue
  772. name, attr_value = item
  773. state[str(name)] = _cache_key_log_value(
  774. attr_value, max_depth=max_depth - 1, max_items=max_items
  775. )
  776. return {"type": _type_log_name(str(module), str(qualname)), "attrs": state}
  777. def _tensor_key_log_value(
  778. names: tuple[str, ...], values: tuple[Any, ...], *, max_depth: int, max_items: int
  779. ) -> dict[str, Any]:
  780. out: dict[str, Any] = {}
  781. for name, value in zip(names, values, strict=False):
  782. if value is None:
  783. continue
  784. out[name] = _cache_key_log_value(
  785. value, max_depth=max_depth - 1, max_items=max_items
  786. )
  787. return out
  788. def _cache_key_log_value(value: Any, *, max_depth: int = 5, max_items: int = 32) -> Any:
  789. if max_depth <= 0:
  790. return _short_repr(value, max_len=120)
  791. if value is None or isinstance(value, (bool, int, float, str)):
  792. return value
  793. if isinstance(value, type):
  794. return _type_log_name(value.__module__, value.__qualname__)
  795. object_state = _object_state_log_value(
  796. value, max_depth=max_depth, max_items=max_items
  797. )
  798. if object_state is not None:
  799. return object_state
  800. if (
  801. isinstance(value, tuple)
  802. and value
  803. and all(
  804. isinstance(item, tuple) and len(item) == 2 and isinstance(item[0], str)
  805. for item in value
  806. )
  807. ):
  808. out: dict[str, Any] = {}
  809. for idx, (key, item_value) in enumerate(value):
  810. if idx >= max_items:
  811. out["..."] = f"{len(value) - idx} more"
  812. break
  813. out[key] = _cache_key_log_value(
  814. item_value, max_depth=max_depth - 1, max_items=max_items
  815. )
  816. return out
  817. if isinstance(value, tuple) and value:
  818. tag = value[0]
  819. if tag == "type" and len(value) == 3:
  820. return _type_log_name(str(value[1]), str(value[2]))
  821. if tag == "function" and len(value) == 2:
  822. return _function_fingerprint_log_value(value[1])
  823. if tag == "method" and len(value) == 3:
  824. return {
  825. "kind": "method",
  826. "function": _function_fingerprint_log_value(value[1]),
  827. "self": _cache_key_log_value(
  828. value[2], max_depth=max_depth - 1, max_items=max_items
  829. ),
  830. }
  831. if tag == "callable_instance" and len(value) == 5:
  832. return {
  833. "kind": "callable_instance",
  834. "type": _type_log_name(str(value[1]), str(value[2])),
  835. "call": _function_fingerprint_log_value(value[3]),
  836. "state": _cache_key_log_value(
  837. value[4], max_depth=max_depth - 1, max_items=max_items
  838. ),
  839. }
  840. if tag == "callable" and len(value) == 4:
  841. return {
  842. "kind": "callable",
  843. "type": _type_log_name(str(value[1]), str(value[2])),
  844. "repr": _short_repr(value[3], max_len=160),
  845. }
  846. if tag == "cache_key" and len(value) == 4:
  847. return {
  848. "type": _type_log_name(str(value[1]), str(value[2])),
  849. "cache_key": _cache_key_log_value(
  850. value[3], max_depth=max_depth - 1, max_items=max_items
  851. ),
  852. }
  853. if tag == "fake_tensor" and len(value) == 12:
  854. return _tensor_key_log_value(
  855. (
  856. "kind",
  857. "type",
  858. "dtype",
  859. "shape",
  860. "stride",
  861. "stride_order",
  862. "device",
  863. "layout",
  864. "memspace",
  865. "align",
  866. "use_32bit_stride",
  867. ),
  868. (
  869. "fake_tensor",
  870. _type_log_name(str(value[1]), str(value[2])),
  871. *value[3:],
  872. ),
  873. max_depth=max_depth,
  874. max_items=max_items,
  875. )
  876. if tag == "runtime_tensor" and len(value) == 8:
  877. return _tensor_key_log_value(
  878. (
  879. "kind",
  880. "dtype",
  881. "shape",
  882. "stride",
  883. "memspace",
  884. "align",
  885. "is_dynamic",
  886. "use_32bit_stride",
  887. ),
  888. value,
  889. max_depth=max_depth,
  890. max_items=max_items,
  891. )
  892. if tag == "fake_compact_tensor" and len(value) == 7:
  893. return _tensor_key_log_value(
  894. (
  895. "kind",
  896. "dtype",
  897. "shape",
  898. "stride_order",
  899. "memspace",
  900. "align",
  901. "use_32bit_stride",
  902. ),
  903. value,
  904. max_depth=max_depth,
  905. max_items=max_items,
  906. )
  907. if tag == "cuda_stream":
  908. return "cuda_stream"
  909. if tag == "symbolic_dim" and len(value) == 4:
  910. return value[3]
  911. if tag == "bytes" and len(value) == 2 and isinstance(value[1], str):
  912. return f"bytes[{len(value[1]) // 2}]"
  913. if tag == "path" and len(value) == 2:
  914. return value[1]
  915. if tag == "repr" and len(value) == 4:
  916. return {
  917. "type": _type_log_name(str(value[1]), str(value[2])),
  918. "repr": _short_repr(value[3], max_len=160),
  919. }
  920. if tag == "cycle" and len(value) == 3:
  921. return {"cycle": _type_log_name(str(value[1]), str(value[2]))}
  922. if isinstance(value, dict):
  923. out = {}
  924. for idx, (key, item_value) in enumerate(
  925. sorted(value.items(), key=lambda kv: str(kv[0]))
  926. ):
  927. if idx >= max_items:
  928. out["..."] = f"{len(value) - idx} more"
  929. break
  930. out[str(key)] = _cache_key_log_value(
  931. item_value, max_depth=max_depth - 1, max_items=max_items
  932. )
  933. return out
  934. if isinstance(value, (tuple, list)):
  935. items = [
  936. _cache_key_log_value(item, max_depth=max_depth - 1, max_items=max_items)
  937. for item in value[:max_items]
  938. ]
  939. if len(value) > max_items:
  940. items.append(f"... {len(value) - max_items} more")
  941. return tuple(items) if isinstance(value, tuple) else items
  942. return _short_repr(value, max_len=160)
  943. def _toolchain_log_value(toolchain: tuple[object, ...]) -> dict[str, Any]:
  944. summary: dict[str, Any] = {}
  945. for entry in toolchain:
  946. if not (isinstance(entry, tuple) and entry):
  947. continue
  948. name = entry[0]
  949. if name == "python" and len(entry) >= 3:
  950. summary[str(name)] = f"{entry[1]} {'.'.join(str(v) for v in entry[2])}"
  951. elif len(entry) >= 2 and entry[1]:
  952. summary[str(name)] = entry[1]
  953. return summary
  954. def _environment_log_value(env: tuple[tuple[str, str], ...]) -> dict[str, str]:
  955. return {name: value for name, value in env if value}
  956. def _compile_cache_payload_log_value(
  957. payload: tuple[object, ...] | None,
  958. ) -> dict[str, Any]:
  959. if payload is None:
  960. return {}
  961. if (
  962. len(payload) == 11
  963. and payload[0] == "b12x_cute_compile_cache_v6_explicit_spec"
  964. ):
  965. (
  966. _version,
  967. target_key,
  968. _b12x_fingerprint,
  969. toolchain_key,
  970. _device_uuid,
  971. spec_hash,
  972. spec_json,
  973. kwargs_hash,
  974. kwargs_json,
  975. options_key,
  976. env_key,
  977. ) = payload
  978. summary: dict[str, Any] = {
  979. "target": _cache_key_log_value(target_key, max_depth=7, max_items=80),
  980. "spec_hash": spec_hash,
  981. "spec": spec_json,
  982. }
  983. if kwargs_hash:
  984. summary["kwargs_hash"] = kwargs_hash
  985. summary["kwargs"] = kwargs_json
  986. if options_key:
  987. summary["options"] = _cache_key_log_value(
  988. options_key, max_depth=4, max_items=32
  989. )
  990. env_summary = (
  991. _environment_log_value(env_key) if isinstance(env_key, tuple) else {}
  992. )
  993. if env_summary:
  994. summary["env"] = env_summary
  995. if isinstance(toolchain_key, tuple):
  996. toolchain_summary = _toolchain_log_value(toolchain_key)
  997. if toolchain_summary:
  998. summary["toolchain"] = toolchain_summary
  999. return summary
  1000. if len(payload) == 8 and payload[0] in {
  1001. "b12x_cute_compile_cache_v3_explicit_spec",
  1002. "b12x_cute_compile_cache_v4_explicit_spec",
  1003. }:
  1004. (
  1005. _version,
  1006. target_key,
  1007. _b12x_fingerprint,
  1008. toolchain_key,
  1009. spec_key,
  1010. kwargs_key,
  1011. options_key,
  1012. env_key,
  1013. ) = payload
  1014. summary: dict[str, Any] = {
  1015. "target": _cache_key_log_value(target_key, max_depth=7, max_items=80),
  1016. "spec": _cache_key_log_value(spec_key, max_depth=7, max_items=80),
  1017. }
  1018. if options_key:
  1019. summary["options"] = _cache_key_log_value(
  1020. options_key, max_depth=4, max_items=32
  1021. )
  1022. kwargs_summary = _cache_key_log_value(kwargs_key, max_depth=5, max_items=32)
  1023. if kwargs_summary:
  1024. summary["kwargs"] = kwargs_summary
  1025. env_summary = (
  1026. _environment_log_value(env_key) if isinstance(env_key, tuple) else {}
  1027. )
  1028. if env_summary:
  1029. summary["env"] = env_summary
  1030. if isinstance(toolchain_key, tuple):
  1031. toolchain_summary = _toolchain_log_value(toolchain_key)
  1032. if toolchain_summary:
  1033. summary["toolchain"] = toolchain_summary
  1034. return summary
  1035. if len(payload) != 9:
  1036. return {}
  1037. (
  1038. _version,
  1039. target_key,
  1040. _b12x_fingerprint,
  1041. toolchain_key,
  1042. _device_uuid,
  1043. args_key,
  1044. kwargs_key,
  1045. options_key,
  1046. env_key,
  1047. ) = payload
  1048. summary: dict[str, Any] = {
  1049. "target": _cache_key_log_value(target_key, max_depth=7, max_items=80),
  1050. "args": _cache_key_log_value(args_key, max_depth=5, max_items=32),
  1051. }
  1052. kwargs_summary = _cache_key_log_value(kwargs_key, max_depth=5, max_items=32)
  1053. if kwargs_summary:
  1054. summary["kwargs"] = kwargs_summary
  1055. if options_key:
  1056. summary["options"] = _cache_key_log_value(
  1057. options_key, max_depth=4, max_items=32
  1058. )
  1059. env_summary = _environment_log_value(env_key) if isinstance(env_key, tuple) else {}
  1060. if env_summary:
  1061. summary["env"] = env_summary
  1062. if isinstance(toolchain_key, tuple):
  1063. toolchain_summary = _toolchain_log_value(toolchain_key)
  1064. if toolchain_summary:
  1065. summary["toolchain"] = toolchain_summary
  1066. return summary
  1067. def _is_explicit_spec_payload(payload: tuple[object, ...] | None) -> bool:
  1068. return (
  1069. payload is not None
  1070. and len(payload) == 11
  1071. and payload[0] == "b12x_cute_compile_cache_v6_explicit_spec"
  1072. )
  1073. def _cute_compile_arg_log_enabled() -> bool:
  1074. raw = os.environ.get("B12X_LOG_CUTE_COMPILE_ARGS", "")
  1075. return raw.lower() not in {"", "0", "false", "no", "off"}
  1076. def _format_explicit_spec_log_details(summary: dict[str, Any]) -> str:
  1077. parts = []
  1078. if "spec_hash" in summary:
  1079. parts.append(f"spec_hash={summary['spec_hash']}")
  1080. if "spec" in summary:
  1081. parts.append(f"spec={summary['spec']}")
  1082. if "kwargs_hash" in summary:
  1083. parts.append(f"kwargs_hash={summary['kwargs_hash']}")
  1084. if "kwargs" in summary:
  1085. parts.append(f"kwargs={summary['kwargs']}")
  1086. if "options" in summary:
  1087. parts.append(f"options={_short_repr(summary['options'], max_len=1200)}")
  1088. if "env" in summary:
  1089. parts.append(f"env={_short_repr(summary['env'], max_len=1200)}")
  1090. if "toolchain" in summary:
  1091. parts.append(f"toolchain={_short_repr(summary['toolchain'], max_len=1200)}")
  1092. return " ".join(parts)
  1093. def _format_cute_compile_stack() -> str:
  1094. depth = _cute_compile_stack_log_depth()
  1095. frames = traceback.extract_stack()[:-2]
  1096. runtime_patch_path = str(Path(__file__).resolve())
  1097. visible_frames = [
  1098. frame
  1099. for frame in frames
  1100. if str(Path(frame.filename).resolve()) != runtime_patch_path
  1101. ]
  1102. if depth:
  1103. visible_frames = visible_frames[-depth:]
  1104. lines = ["[b12x cute.compile] python_stack (most recent call last):"]
  1105. for frame in visible_frames:
  1106. lines.append(f' File "{frame.filename}", line {frame.lineno}, in {frame.name}')
  1107. if frame.line:
  1108. lines.append(f" {frame.line.strip()}")
  1109. return "\n".join(lines)
  1110. def _log_cute_compile_event(
  1111. func: Any,
  1112. args: tuple[Any, ...],
  1113. kwargs: dict[str, Any],
  1114. *,
  1115. event: str,
  1116. cache_status: str,
  1117. cache_payload: tuple[object, ...] | None = None,
  1118. reason: str | None = None,
  1119. cache_key: str | None = None,
  1120. ) -> None:
  1121. key_inputs = _compile_cache_payload_log_value(cache_payload)
  1122. reason_text = "" if reason is None else f"reason={reason} "
  1123. cache_key_text = "" if cache_key is None else f"cache_key={cache_key[:16]} "
  1124. if _is_explicit_spec_payload(cache_payload):
  1125. attrs_text = _short_repr(_compile_target_attrs(func), max_len=1200)
  1126. args_text = ""
  1127. if _cute_compile_arg_log_enabled():
  1128. args_text = f"args={_short_repr(_compile_args_shape_summary(args, kwargs), max_len=1600)} "
  1129. details = _format_explicit_spec_log_details(key_inputs)
  1130. print(
  1131. f"[b12x cute.compile] {event} "
  1132. f"{reason_text}"
  1133. f"target={_compile_target_name(func)} "
  1134. f"status={cache_status} "
  1135. f"{cache_key_text}"
  1136. f"attrs={attrs_text} "
  1137. f"{args_text}"
  1138. f"{details}",
  1139. flush=True,
  1140. )
  1141. if _cute_compile_stack_log_enabled():
  1142. print(_format_cute_compile_stack(), flush=True)
  1143. return
  1144. print(
  1145. f"[b12x cute.compile] {event} "
  1146. f"{reason_text}"
  1147. f"target={_compile_target_name(func)} "
  1148. f"status={cache_status} "
  1149. f"{cache_key_text}"
  1150. f"attrs={_short_repr(_compile_target_attrs(func), max_len=1200)} "
  1151. f"args={_short_repr(_compile_args_shape_summary(args, kwargs), max_len=1600)} "
  1152. f"key_inputs={_short_repr(key_inputs, max_len=4000)}",
  1153. flush=True,
  1154. )
  1155. if _cute_compile_stack_log_enabled():
  1156. print(_format_cute_compile_stack(), flush=True)
  1157. def _log_cute_compile_miss(
  1158. func: Any,
  1159. args: tuple[Any, ...],
  1160. kwargs: dict[str, Any],
  1161. *,
  1162. cache_status: str,
  1163. cache_payload: tuple[object, ...] | None = None,
  1164. reason: str | None = None,
  1165. cache_key: str | None = None,
  1166. ) -> None:
  1167. _log_cute_compile_event(
  1168. func,
  1169. args,
  1170. kwargs,
  1171. event="miss",
  1172. cache_status=cache_status,
  1173. cache_payload=cache_payload,
  1174. reason=reason,
  1175. cache_key=cache_key,
  1176. )
  1177. def _compile_progress_details(
  1178. func: Any,
  1179. args: tuple[Any, ...],
  1180. kwargs: dict[str, Any],
  1181. *,
  1182. compile_spec: KernelCompileSpec | None,
  1183. cache_key: str,
  1184. ) -> str:
  1185. parts = [
  1186. f"target={_compile_target_name(func)}",
  1187. f"cache_key={cache_key[:16]}",
  1188. ]
  1189. if compile_spec is not None:
  1190. try:
  1191. params = json.loads(compile_spec.json_key)["facts"]
  1192. except Exception:
  1193. params = compile_spec.json_key
  1194. parts.extend(
  1195. (
  1196. f"kernel={compile_spec.kernel_id}",
  1197. f"version={compile_spec.version}",
  1198. f"params={_short_repr(params, max_len=2400)}",
  1199. )
  1200. )
  1201. else:
  1202. attrs = _compile_target_attrs(func)
  1203. if attrs:
  1204. parts.append(f"attrs={_short_repr(attrs, max_len=1200)}")
  1205. parts.append(
  1206. f"args={_short_repr(_compile_args_shape_summary(args, kwargs), max_len=2000)}"
  1207. )
  1208. return " ".join(parts)
  1209. def _call_cute_compile(
  1210. compile_callable: Any,
  1211. func: Any,
  1212. args: tuple[Any, ...],
  1213. kwargs: dict[str, Any],
  1214. *,
  1215. compile_spec: KernelCompileSpec | None,
  1216. cache_key: str,
  1217. ) -> Any:
  1218. if not _cute_compile_progress_enabled():
  1219. return compile_callable(func, *args, **kwargs)
  1220. global _COMPILE_PROGRESS_COUNT
  1221. global _COMPILE_PROGRESS_TOTAL_SECONDS
  1222. with _COMPILE_PROGRESS_LOCK:
  1223. _COMPILE_PROGRESS_COUNT += 1
  1224. compile_number = _COMPILE_PROGRESS_COUNT
  1225. try:
  1226. details = _compile_progress_details(
  1227. func,
  1228. args,
  1229. kwargs,
  1230. compile_spec=compile_spec,
  1231. cache_key=cache_key,
  1232. )
  1233. except Exception as exc:
  1234. details = (
  1235. f"target={_short_repr(func)} cache_key={cache_key[:16]} "
  1236. f"details_error={type(exc).__name__}"
  1237. )
  1238. print(
  1239. f"[b12x cute.compile] compile-start number={compile_number} {details}",
  1240. flush=True,
  1241. )
  1242. started = time.perf_counter()
  1243. try:
  1244. compiled = compile_callable(func, *args, **kwargs)
  1245. except BaseException as exc:
  1246. elapsed = time.perf_counter() - started
  1247. with _COMPILE_PROGRESS_LOCK:
  1248. _COMPILE_PROGRESS_TOTAL_SECONDS += elapsed
  1249. total = _COMPILE_PROGRESS_TOTAL_SECONDS
  1250. print(
  1251. f"[b12x cute.compile] compile-failed number={compile_number} "
  1252. f"duration_s={elapsed:.3f} total_compile_s={total:.3f} {details} "
  1253. f"error={type(exc).__name__}: {_short_repr(exc, max_len=500)}",
  1254. flush=True,
  1255. )
  1256. raise
  1257. elapsed = time.perf_counter() - started
  1258. with _COMPILE_PROGRESS_LOCK:
  1259. _COMPILE_PROGRESS_TOTAL_SECONDS += elapsed
  1260. total = _COMPILE_PROGRESS_TOTAL_SECONDS
  1261. print(
  1262. f"[b12x cute.compile] compile-done number={compile_number} "
  1263. f"duration_s={elapsed:.3f} total_compile_s={total:.3f} {details}",
  1264. flush=True,
  1265. )
  1266. return compiled
  1267. def _iter_fingerprint_files(root: Path) -> list[Path]:
  1268. files = []
  1269. for path in root.rglob("*"):
  1270. if not path.is_file():
  1271. continue
  1272. if "__pycache__" in path.parts:
  1273. continue
  1274. if path.suffix in {".pyc", ".pyo"}:
  1275. continue
  1276. files.append(path)
  1277. files.sort()
  1278. return files
  1279. def _compute_b12x_package_fingerprint() -> str:
  1280. digest = hashlib.sha256()
  1281. for path in _iter_fingerprint_files(_PACKAGE_ROOT):
  1282. rel_path = str(path.relative_to(_PACKAGE_ROOT))
  1283. digest.update(rel_path.encode("utf-8"))
  1284. digest.update(b"\0")
  1285. digest.update(path.read_bytes())
  1286. digest.update(b"\0")
  1287. return digest.hexdigest()
  1288. @lru_cache(maxsize=1)
  1289. def _b12x_package_fingerprint() -> str:
  1290. return _compute_b12x_package_fingerprint()
  1291. def b12x_package_fingerprint() -> str:
  1292. """Return the exact content fingerprint used by the CuTe object cache."""
  1293. return _b12x_package_fingerprint()
  1294. def _distribution_version(name: str) -> str:
  1295. try:
  1296. return importlib.metadata.version(name)
  1297. except importlib.metadata.PackageNotFoundError:
  1298. return ""
  1299. _DEVICE_UUID_KEYS: dict[int, tuple[str, str]] = {}
  1300. _DEVICE_COMPILE_CACHE_CONTEXTS: dict[tuple[Any, int], tuple[object, ...]] = {}
  1301. def _current_device_ordinal() -> int | None:
  1302. """Ordinal of the device this process is currently compiling for."""
  1303. try:
  1304. import torch
  1305. if not torch.cuda.is_available():
  1306. return None
  1307. return int(torch.cuda.current_device())
  1308. except Exception: # noqa: BLE001 - cache identity probing must fail closed
  1309. return None
  1310. def _device_uuid_key(device_ordinal: int | None = None) -> tuple[str, str] | None:
  1311. """Return the physical CUDA device UUID, memoized by visible ordinal.
  1312. Ordinals and distributed ranks are process-local and can be remapped by
  1313. ``CUDA_VISIBLE_DEVICES``. The UUID is the persistent identity used by the
  1314. disk payload (and therefore by non-explicit memory keys). Explicit-spec
  1315. memory hits stay on their existing spec-only hot path. Probe failures return
  1316. ``None`` and are deliberately not memoized so callers can disable disk
  1317. reuse and retry on the next compile.
  1318. """
  1319. index = None if device_ordinal is None else int(device_ordinal)
  1320. if index is not None:
  1321. cached = _DEVICE_UUID_KEYS.get(index)
  1322. if cached is not None:
  1323. return cached
  1324. try:
  1325. import torch
  1326. if not torch.cuda.is_available():
  1327. return None
  1328. if index is None:
  1329. index = int(torch.cuda.current_device())
  1330. cached = _DEVICE_UUID_KEYS.get(index)
  1331. if cached is not None:
  1332. return cached
  1333. raw_uuid = getattr(torch.cuda.get_device_properties(index), "uuid", None)
  1334. device_uuid = "" if raw_uuid is None else str(raw_uuid).strip()
  1335. if not device_uuid:
  1336. return None
  1337. except Exception: # noqa: BLE001 - cache identity probing must fail closed
  1338. return None
  1339. key = ("device_uuid", device_uuid)
  1340. _DEVICE_UUID_KEYS[index] = key
  1341. return key
  1342. @lru_cache(maxsize=1)
  1343. def _runtime_toolchain_key() -> tuple[object, ...]:
  1344. from .runtime_patches import cutlass_runtime_patch_status
  1345. torch_version = _distribution_version("torch")
  1346. torch_cuda_version = ""
  1347. try:
  1348. import torch
  1349. if not torch_version:
  1350. torch_version = getattr(torch, "__version__", "")
  1351. torch_cuda_version = getattr(torch.version, "cuda", "") or ""
  1352. except Exception:
  1353. pass
  1354. cutlass_version = _distribution_version("nvidia-cutlass-dsl")
  1355. if not cutlass_version:
  1356. cutlass_version = _distribution_version("cutlass")
  1357. if not cutlass_version:
  1358. try:
  1359. import cutlass
  1360. cutlass_version = getattr(cutlass, "__version__", "")
  1361. except Exception:
  1362. cutlass_version = ""
  1363. cutlass_version = cutlass_version or "missing"
  1364. return (
  1365. ("python", sys.implementation.name, sys.version_info[:3]),
  1366. ("torch", torch_version),
  1367. ("torch_cuda", torch_cuda_version),
  1368. ("cutlass_dsl", cutlass_version),
  1369. (
  1370. "cutlass_dsl_libs_base",
  1371. _distribution_version("nvidia-cutlass-dsl-libs-base") or "missing",
  1372. ),
  1373. (
  1374. "cutlass_dsl_libs_core",
  1375. _distribution_version("nvidia-cutlass-dsl-libs-core") or "missing",
  1376. ),
  1377. (
  1378. "cutlass_dsl_libs_cu12",
  1379. _distribution_version("nvidia-cutlass-dsl-libs-cu12") or "missing",
  1380. ),
  1381. (
  1382. "cutlass_dsl_libs_cu13",
  1383. _distribution_version("nvidia-cutlass-dsl-libs-cu13") or "missing",
  1384. ),
  1385. ("cuda_python", _distribution_version("cuda-python")),
  1386. ("cuda_bindings", _distribution_version("cuda-bindings")),
  1387. ("b12x_runtime_patches", cutlass_runtime_patch_status()),
  1388. )
  1389. @lru_cache(maxsize=1)
  1390. def _compile_environment_key() -> tuple[tuple[str, str], ...]:
  1391. compile_env_vars = {
  1392. "CC",
  1393. "CXX",
  1394. "CUDA_HOME",
  1395. "CUDA_PATH",
  1396. "CUDA_TOOLKIT_PATH",
  1397. "CUDACXX",
  1398. "CUTE_DSL_ARCH",
  1399. "NVCC_APPEND_FLAGS",
  1400. "NVCC_PREPEND_FLAGS",
  1401. }
  1402. operational_env_vars = {
  1403. "B12X_COMPILE_CACHE_DIR",
  1404. "B12X_COMPILE_DISK_CACHE",
  1405. "B12X_COMPILE_MEMORY_CACHE",
  1406. "B12X_COMPILE_MEMORY_CACHE_SIZE",
  1407. "B12X_COMPILE_SPEC_MEMO",
  1408. "B12X_LOG_CUTE_COMPILES",
  1409. "B12X_LOG_CUTE_COMPILES_AFTER_ENGINE_START",
  1410. "B12X_LOG_CUTE_COMPILE_ARGS",
  1411. "B12X_LOG_CUTE_COMPILE_STACK",
  1412. "B12X_LOG_CUTE_COMPILE_STACK_DEPTH",
  1413. "B12X_PRINT_COMPILE_PROGRESS",
  1414. "B12X_TIMING",
  1415. "B12X_TIMING_THRESHOLD_MS",
  1416. "CUTE_DSL_CACHE_DIR",
  1417. }
  1418. for name in os.environ:
  1419. if name.startswith(("B12X_", "CUTE_", "CUTLASS_")):
  1420. if name not in operational_env_vars:
  1421. compile_env_vars.add(name)
  1422. return tuple((name, os.environ.get(name, "")) for name in sorted(compile_env_vars))
  1423. @lru_cache(maxsize=16)
  1424. def _static_compile_cache_context(compile_callable: Any) -> tuple[object, ...]:
  1425. return (
  1426. _b12x_package_fingerprint(),
  1427. _runtime_toolchain_key(),
  1428. _compile_options_cache_key(compile_callable),
  1429. _compile_environment_key(),
  1430. )
  1431. def _device_compile_cache_context(compile_callable: Any) -> tuple[object, ...]:
  1432. """Return the full static context, caching only successful UUID probes."""
  1433. device_ordinal = _current_device_ordinal()
  1434. context_key = (
  1435. None if device_ordinal is None else (compile_callable, int(device_ordinal))
  1436. )
  1437. if context_key is not None:
  1438. cached = _DEVICE_COMPILE_CACHE_CONTEXTS.get(context_key)
  1439. if cached is not None:
  1440. return cached
  1441. (
  1442. package_fingerprint,
  1443. runtime_toolchain,
  1444. compile_options,
  1445. compile_environment,
  1446. ) = _static_compile_cache_context(compile_callable)
  1447. device_uuid = _device_uuid_key(device_ordinal)
  1448. context = (
  1449. package_fingerprint,
  1450. runtime_toolchain,
  1451. device_uuid,
  1452. compile_options,
  1453. compile_environment,
  1454. )
  1455. if context_key is not None and device_uuid is not None:
  1456. _DEVICE_COMPILE_CACHE_CONTEXTS[context_key] = context
  1457. return context
  1458. def _function_fingerprint(func: Any) -> tuple[str, str, str]:
  1459. func = inspect.unwrap(func)
  1460. module = getattr(func, "__module__", "")
  1461. qualname = getattr(
  1462. func, "__qualname__", getattr(func, "__name__", type(func).__qualname__)
  1463. )
  1464. if module.startswith("b12x"):
  1465. return module, qualname, f"b12x:{_b12x_package_fingerprint()}"
  1466. try:
  1467. source = inspect.getsource(func)
  1468. payload = source.encode("utf-8")
  1469. except (OSError, TypeError):
  1470. code = getattr(func, "__code__", None)
  1471. if code is None:
  1472. payload = repr(func).encode("utf-8")
  1473. else:
  1474. payload = repr(
  1475. (
  1476. code.co_code,
  1477. code.co_consts,
  1478. code.co_names,
  1479. code.co_varnames,
  1480. code.co_argcount,
  1481. code.co_kwonlyargcount,
  1482. )
  1483. ).encode("utf-8")
  1484. digest = hashlib.sha256(payload).hexdigest()
  1485. return module, qualname, digest
  1486. def _normalize_compile_target(func: Any, visited: set[int]) -> Any:
  1487. if inspect.ismethod(func):
  1488. return (
  1489. "method",
  1490. _function_fingerprint(func.__func__),
  1491. _structural_cache_key(func.__self__, visited),
  1492. )
  1493. if inspect.isfunction(func):
  1494. return ("function", _function_fingerprint(func))
  1495. if callable(func) and hasattr(func.__call__, "__func__"):
  1496. state = vars(func) if hasattr(func, "__dict__") else None
  1497. return (
  1498. "callable_instance",
  1499. type(func).__module__,
  1500. type(func).__qualname__,
  1501. _function_fingerprint(func.__call__.__func__),
  1502. _structural_cache_key(state, visited),
  1503. )
  1504. return ("callable", type(func).__module__, type(func).__qualname__, repr(func))
  1505. def _explicit_spec_compile_target(func: Any) -> Any:
  1506. if inspect.ismethod(func):
  1507. return ("method", _function_fingerprint(func.__func__))
  1508. if inspect.isfunction(func):
  1509. return ("function", _function_fingerprint(func))
  1510. if callable(func) and hasattr(func.__call__, "__func__"):
  1511. return (
  1512. "callable_instance",
  1513. type(func).__module__,
  1514. type(func).__qualname__,
  1515. _function_fingerprint(func.__call__.__func__),
  1516. )
  1517. return ("callable", type(func).__module__, type(func).__qualname__)
  1518. def _structural_dim_key(dim: Any, visited: set[int]) -> Any:
  1519. if dim is None or isinstance(dim, (bool, int, float, str)):
  1520. return dim
  1521. try:
  1522. return int(dim)
  1523. except (TypeError, ValueError):
  1524. pass
  1525. label = None
  1526. for attr in ("symbol", "_symbol", "name", "_name"):
  1527. value = getattr(dim, attr, None)
  1528. if isinstance(value, str) and value:
  1529. label = value
  1530. break
  1531. if label is None:
  1532. node = getattr(dim, "node", None)
  1533. expr = getattr(node, "expr", getattr(node, "_expr", None))
  1534. if expr is not None:
  1535. label = str(expr)
  1536. if label is None:
  1537. text = str(dim)
  1538. if text and not text.startswith("?{"):
  1539. label = text
  1540. if label is not None:
  1541. return (
  1542. "symbolic_dim",
  1543. type(dim).__module__,
  1544. type(dim).__qualname__,
  1545. label,
  1546. )
  1547. return (
  1548. "symbolic_dim",
  1549. type(dim).__module__,
  1550. type(dim).__qualname__,
  1551. id(getattr(dim, "node", dim)),
  1552. )
  1553. def _maybe_call_zero_arg(value: Any) -> Any:
  1554. if callable(value):
  1555. try:
  1556. return value()
  1557. except TypeError:
  1558. return None
  1559. return value
  1560. def _first_present_attr(value: Any, *names: str) -> Any:
  1561. for name in names:
  1562. try:
  1563. attr = getattr(value, name)
  1564. except Exception:
  1565. continue
  1566. attr = _maybe_call_zero_arg(attr)
  1567. if attr is not None:
  1568. return attr
  1569. return None
  1570. def _tensor_like_cache_key(value: Any, visited: set[int]) -> Any | None:
  1571. shape = _first_present_attr(value, "_shape", "shape")
  1572. if shape is None:
  1573. return None
  1574. stride = _first_present_attr(value, "_stride", "stride")
  1575. stride_order = _first_present_attr(value, "_stride_order", "stride_order")
  1576. dtype = _first_present_attr(value, "_dtype", "dtype", "element_type")
  1577. device = _first_present_attr(value, "fake_device", "device")
  1578. layout = _first_present_attr(value, "layout")
  1579. memspace = _first_present_attr(value, "memspace", "_memspace")
  1580. assumed_align = _first_present_attr(value, "_assumed_align")
  1581. use_32bit_stride = _first_present_attr(value, "_use_32bit_stride")
  1582. shape_key = tuple(_structural_dim_key(dim, visited) for dim in shape)
  1583. stride_key = (
  1584. None
  1585. if stride is None
  1586. else tuple(_structural_dim_key(dim, visited) for dim in stride)
  1587. )
  1588. stride_order_key = (
  1589. None
  1590. if stride_order is None
  1591. else tuple(_structural_dim_key(dim, visited) for dim in stride_order)
  1592. )
  1593. return (
  1594. "fake_tensor",
  1595. type(value).__module__,
  1596. type(value).__qualname__,
  1597. _structural_cache_key(dtype, visited),
  1598. shape_key,
  1599. stride_key,
  1600. stride_order_key,
  1601. _structural_cache_key(device, visited),
  1602. _structural_cache_key(layout, visited),
  1603. _structural_cache_key(memspace, visited),
  1604. assumed_align,
  1605. use_32bit_stride,
  1606. )
  1607. def _structural_cache_key(value: Any, visited: set[int] | None = None) -> Any:
  1608. if visited is None:
  1609. visited = set()
  1610. if value is None or isinstance(value, (bool, int, float, str)):
  1611. return value
  1612. if isinstance(value, bytes):
  1613. return ("bytes", value.hex())
  1614. if isinstance(value, Path):
  1615. return ("path", str(value))
  1616. if inspect.isfunction(value) or inspect.ismethod(value):
  1617. return _normalize_compile_target(value, visited)
  1618. if isinstance(value, type):
  1619. return ("type", value.__module__, value.__qualname__)
  1620. if isinstance(value, SimpleNamespace):
  1621. return (
  1622. "namespace",
  1623. tuple(
  1624. sorted(
  1625. (k, _structural_cache_key(v, visited))
  1626. for k, v in vars(value).items()
  1627. )
  1628. ),
  1629. )
  1630. if isinstance(value, dict):
  1631. return tuple(
  1632. sorted(
  1633. (_structural_cache_key(k, visited), _structural_cache_key(v, visited))
  1634. for k, v in value.items()
  1635. )
  1636. )
  1637. if isinstance(value, (tuple, list)):
  1638. return tuple(_structural_cache_key(v, visited) for v in value)
  1639. if isinstance(value, set):
  1640. return tuple(sorted(_structural_cache_key(v, visited) for v in value))
  1641. type_name = type(value).__name__
  1642. type_module = type(value).__module__
  1643. if type_name == "CUstream" and type_module.startswith("cuda.bindings"):
  1644. return ("cuda_stream",)
  1645. if type_module == "cutlass.cute.runtime" and type_name == "_Tensor":
  1646. dtype = getattr(value, "_dtype", getattr(value, "element_type", None))
  1647. shape = tuple(_structural_dim_key(dim, visited) for dim in value.shape)
  1648. stride = tuple(_structural_dim_key(dim, visited) for dim in value.stride)
  1649. memspace = getattr(value, "memspace", getattr(value, "_memspace", None))
  1650. assumed_align = getattr(value, "_assumed_align", None)
  1651. is_dynamic = getattr(value, "_is_dynamic", None)
  1652. use_32bit_stride = getattr(value, "_use_32bit_stride", None)
  1653. return (
  1654. "runtime_tensor",
  1655. dtype,
  1656. shape,
  1657. stride,
  1658. memspace,
  1659. assumed_align,
  1660. is_dynamic,
  1661. use_32bit_stride,
  1662. )
  1663. if type_module == "cutlass.cute.runtime" and type_name == "_FakeCompactTensor":
  1664. dtype = getattr(value, "_dtype", None)
  1665. shape = tuple(
  1666. _structural_dim_key(dim, visited) for dim in getattr(value, "_shape", ())
  1667. )
  1668. stride_order = tuple(
  1669. _structural_dim_key(dim, visited)
  1670. for dim in getattr(value, "_stride_order", ())
  1671. )
  1672. memspace = getattr(value, "_memspace", None)
  1673. assumed_align = getattr(value, "_assumed_align", None)
  1674. use_32bit_stride = getattr(value, "_use_32bit_stride", None)
  1675. return (
  1676. "fake_compact_tensor",
  1677. dtype,
  1678. shape,
  1679. stride_order,
  1680. memspace,
  1681. assumed_align,
  1682. use_32bit_stride,
  1683. )
  1684. if "FakeTensor" in type_name:
  1685. fake_tensor_key = _tensor_like_cache_key(value, visited)
  1686. if fake_tensor_key is not None:
  1687. return fake_tensor_key
  1688. cache_key_attr = getattr(value, "__cache_key__", None)
  1689. if cache_key_attr is not None:
  1690. return (
  1691. "cache_key",
  1692. type_module,
  1693. type_name,
  1694. _structural_cache_key(cache_key_attr, visited),
  1695. )
  1696. object_id = id(value)
  1697. if object_id in visited:
  1698. return ("cycle", type_module, type_name)
  1699. if hasattr(value, "__dict__"):
  1700. visited.add(object_id)
  1701. try:
  1702. return (
  1703. "object",
  1704. type_module,
  1705. type_name,
  1706. tuple(
  1707. sorted(
  1708. (
  1709. k,
  1710. _structural_cache_key(v, visited),
  1711. )
  1712. for k, v in vars(value).items()
  1713. )
  1714. ),
  1715. )
  1716. finally:
  1717. visited.remove(object_id)
  1718. return ("repr", type_module, type_name, repr(value))
  1719. def _compile_options_cache_key(compile_callable: Any) -> tuple[str, ...]:
  1720. compile_options = getattr(compile_callable, "_compile_options", None)
  1721. if compile_options is None:
  1722. return ()
  1723. options = getattr(compile_options, "options", {})
  1724. serialized = []
  1725. for option in options.values():
  1726. value = option.serialize()
  1727. if value:
  1728. serialized.append(value)
  1729. return tuple(serialized)
  1730. def _compile_disk_cache_payload(
  1731. compile_callable: Any,
  1732. func: Any,
  1733. args: tuple[Any, ...],
  1734. kwargs: dict[str, Any],
  1735. compile_spec: KernelCompileSpec | None = None,
  1736. ) -> tuple[object, ...]:
  1737. (
  1738. package_fingerprint,
  1739. runtime_toolchain,
  1740. device_uuid,
  1741. compile_options,
  1742. compile_environment,
  1743. ) = _device_compile_cache_context(compile_callable)
  1744. if compile_spec is not None:
  1745. kwargs_json_key, kwargs_hash_key = _compile_kwargs_json_key(kwargs)
  1746. return (
  1747. "b12x_cute_compile_cache_v6_explicit_spec",
  1748. _explicit_spec_compile_target(func),
  1749. package_fingerprint,
  1750. runtime_toolchain,
  1751. device_uuid,
  1752. compile_spec.hash_key,
  1753. compile_spec.json_key,
  1754. kwargs_hash_key,
  1755. kwargs_json_key,
  1756. compile_options,
  1757. compile_environment,
  1758. )
  1759. return (
  1760. "b12x_cute_compile_cache_v3",
  1761. _normalize_compile_target(func, set()),
  1762. package_fingerprint,
  1763. runtime_toolchain,
  1764. device_uuid,
  1765. _structural_cache_key(args),
  1766. _structural_cache_key(kwargs),
  1767. compile_options,
  1768. compile_environment,
  1769. )
  1770. def _build_compile_disk_cache_key(
  1771. compile_callable: Any,
  1772. func: Any,
  1773. args: tuple[Any, ...],
  1774. kwargs: dict[str, Any],
  1775. compile_spec: KernelCompileSpec | None = None,
  1776. ) -> str:
  1777. payload = _compile_disk_cache_payload(
  1778. compile_callable, func, args, kwargs, compile_spec
  1779. )
  1780. return hashlib.sha256(repr(payload).encode("utf-8")).hexdigest()
  1781. def _cache_prefix(cache_key: str) -> str:
  1782. return f"b12x_cute_{cache_key}"
  1783. def _cache_object_path(cache_key: str) -> Path:
  1784. return _cute_compile_cache_dir() / cache_key[:2] / f"{cache_key}.o"
  1785. def _cache_manifest_path(cache_key: str) -> Path:
  1786. return _cache_object_path(cache_key).with_suffix(".json")
  1787. def _cache_lock_path(cache_key: str) -> Path:
  1788. return _cache_object_path(cache_key).with_suffix(".lock")
  1789. def _manifest_json_value(value: Any) -> Any:
  1790. """Return a lossless-enough JSON view of an already-normalized cache value.
  1791. Compile-cache payloads consist almost entirely of tuples and JSON scalar
  1792. values. Keep an explicit representation for uncommon values instead of
  1793. allowing a sidecar write failure to discard the useful semantic fields.
  1794. The exact payload used to form ``cache_key`` is also stored as ``repr`` in
  1795. the manifest.
  1796. """
  1797. if value is None or isinstance(value, (bool, int, str)):
  1798. return value
  1799. if isinstance(value, float):
  1800. return value if math.isfinite(value) else {"kind": "float", "repr": repr(value)}
  1801. if isinstance(value, bytes):
  1802. return {"kind": "bytes", "hex": value.hex()}
  1803. if isinstance(value, Path):
  1804. return {"kind": "path", "value": str(value)}
  1805. if isinstance(value, (tuple, list)):
  1806. return [_manifest_json_value(item) for item in value]
  1807. if isinstance(value, dict):
  1808. if all(isinstance(key, str) for key in value):
  1809. return {
  1810. key: _manifest_json_value(item) for key, item in sorted(value.items())
  1811. }
  1812. return {
  1813. "kind": "mapping",
  1814. "items": [
  1815. [_manifest_json_value(key), _manifest_json_value(item)]
  1816. for key, item in sorted(value.items(), key=lambda pair: repr(pair[0]))
  1817. ],
  1818. }
  1819. return {
  1820. "kind": "repr",
  1821. "type": f"{type(value).__module__}.{type(value).__qualname__}",
  1822. "value": repr(value),
  1823. }
  1824. def _semantic_target_key(target_key: Any) -> Any:
  1825. """Remove source fingerprints while retaining callable semantic state."""
  1826. if not isinstance(target_key, tuple) or not target_key:
  1827. return _manifest_json_value(target_key)
  1828. tag = target_key[0]
  1829. if tag in {"method", "function"} and len(target_key) >= 2:
  1830. fingerprint = target_key[1]
  1831. if isinstance(fingerprint, tuple) and len(fingerprint) >= 2:
  1832. result: dict[str, Any] = {
  1833. "kind": tag,
  1834. "module": str(fingerprint[0]),
  1835. "qualname": str(fingerprint[1]),
  1836. }
  1837. if len(target_key) >= 3:
  1838. result["state"] = _semantic_structural_key(target_key[2])
  1839. return result
  1840. if tag == "callable_instance" and len(target_key) >= 4:
  1841. call_fingerprint = target_key[3]
  1842. result = {
  1843. "kind": tag,
  1844. "type": f"{target_key[1]}.{target_key[2]}",
  1845. }
  1846. if isinstance(call_fingerprint, tuple) and len(call_fingerprint) >= 2:
  1847. result["call"] = f"{call_fingerprint[0]}.{call_fingerprint[1]}"
  1848. else:
  1849. result["call"] = _semantic_structural_key(call_fingerprint)
  1850. if len(target_key) >= 5:
  1851. result["state"] = _semantic_structural_key(target_key[4])
  1852. return result
  1853. if tag == "callable" and len(target_key) >= 3:
  1854. return {
  1855. "kind": tag,
  1856. "type": f"{target_key[1]}.{target_key[2]}",
  1857. }
  1858. return _semantic_structural_key(target_key)
  1859. def _semantic_structural_key(value: Any) -> Any:
  1860. if isinstance(value, tuple):
  1861. if value and value[0] in {
  1862. "method",
  1863. "function",
  1864. "callable_instance",
  1865. "callable",
  1866. }:
  1867. return _semantic_target_key(value)
  1868. return [_semantic_structural_key(item) for item in value]
  1869. if isinstance(value, list):
  1870. return [_semantic_structural_key(item) for item in value]
  1871. return _manifest_json_value(value)
  1872. def _semantic_compile_manifest_payload(
  1873. cache_payload: tuple[object, ...],
  1874. ) -> dict[str, Any]:
  1875. cache_format = str(cache_payload[0]) if cache_payload else "unknown"
  1876. semantic: dict[str, Any] = {
  1877. "cache_format": cache_format,
  1878. "target": _semantic_target_key(cache_payload[1]),
  1879. # device_uuid is cache_payload index 4 in both the v6_explicit_spec and
  1880. # v3 formats. The persistent identity intentionally isolates artifacts
  1881. # by physical GPU rather than by process-local ordinal or rank.
  1882. "device_uuid": _manifest_json_value(cache_payload[4]),
  1883. }
  1884. if cache_format == "b12x_cute_compile_cache_v6_explicit_spec":
  1885. semantic["compile_spec_hash"] = cache_payload[5]
  1886. try:
  1887. semantic["compile_spec"] = json.loads(str(cache_payload[6]))
  1888. except (TypeError, ValueError, json.JSONDecodeError):
  1889. semantic["compile_spec"] = str(cache_payload[6])
  1890. if cache_payload[7]:
  1891. semantic["compile_kwargs_hash"] = cache_payload[7]
  1892. try:
  1893. semantic["compile_kwargs"] = json.loads(str(cache_payload[8]))
  1894. except (TypeError, ValueError, json.JSONDecodeError):
  1895. semantic["compile_kwargs"] = str(cache_payload[8])
  1896. semantic["compile_options"] = _manifest_json_value(cache_payload[9])
  1897. semantic["compile_environment"] = _manifest_json_value(cache_payload[10])
  1898. else:
  1899. semantic["args"] = _semantic_structural_key(cache_payload[5])
  1900. semantic["kwargs"] = _semantic_structural_key(cache_payload[6])
  1901. semantic["compile_options"] = _manifest_json_value(cache_payload[7])
  1902. semantic["compile_environment"] = _manifest_json_value(cache_payload[8])
  1903. return semantic
  1904. _LLVM_SSA_NAME = r"%[-a-zA-Z$._0-9]+"
  1905. _LLVM_I64_CONSTANT_RE = re.compile(
  1906. rf"^\s*(?P<result>{_LLVM_SSA_NAME}) = "
  1907. r"llvm\.mlir\.constant\((?P<value>[0-9]+) : i64\) : i64\s*$"
  1908. )
  1909. _LLVM_GEP_RE = re.compile(
  1910. rf"^\s*(?P<result>{_LLVM_SSA_NAME}) = llvm\.getelementptr "
  1911. rf"(?P<base>{_LLVM_SSA_NAME})\[(?P<indices>[^]]+)\]"
  1912. )
  1913. _LLVM_LOAD_RE = re.compile(
  1914. rf"^\s*(?P<result>{_LLVM_SSA_NAME}) = llvm\.load "
  1915. rf"(?P<pointer>{_LLVM_SSA_NAME})(?:\s|:)"
  1916. )
  1917. _LLVM_STORE_RE = re.compile(
  1918. rf"^\s*llvm\.store (?P<value>{_LLVM_SSA_NAME}), "
  1919. rf"(?P<pointer>{_LLVM_SSA_NAME})(?:\s|:)"
  1920. )
  1921. _LLVM_KERNEL_ADDRESS_RE = re.compile(
  1922. rf"^\s*(?P<result>{_LLVM_SSA_NAME}) = llvm\.mlir\.addressof "
  1923. r"@kernels_(?P<kernel>[^\s:]+)\s*:"
  1924. )
  1925. _LLVM_LAUNCH_EX_RE = re.compile(
  1926. r"llvm\.call @_cudaLaunchKernelEx\((?P<arguments>[^)]*)\)"
  1927. )
  1928. def _final_llvm_function_bodies(module_text: str) -> list[list[str]]:
  1929. """Return lowered LLVM function bodies without parsing the embedded cubin.
  1930. CUTLASS' final module contains the cubin as one very large LLVM global.
  1931. Launch configuration is emitted in host ``llvm.func`` bodies, whose closing
  1932. brace is at module indentation. Keeping the scan line-oriented avoids
  1933. retaining or reparsing a second MLIR module during every compilation.
  1934. """
  1935. bodies: list[list[str]] = []
  1936. current: list[str] | None = None
  1937. for line in module_text.splitlines():
  1938. if current is None:
  1939. if line.startswith(" llvm.func @") and line.rstrip().endswith("{"):
  1940. current = [line]
  1941. continue
  1942. current.append(line)
  1943. if line == " }":
  1944. bodies.append(current)
  1945. current = None
  1946. return bodies
  1947. def _parse_launch_dynamic_smem_from_final_llvm(
  1948. module_text: str, expected_kernels: set[str]
  1949. ) -> dict[str, Any]:
  1950. """Extract exact resolved CUDA launch SMEM from CUTLASS final LLVM IR.
  1951. CUTLASS 4.5 and 4.6 lower ``cute.kernel_smem_size`` to an i64 constant
  1952. stored in field 2 of the CUDA launch-config struct. The exported object
  1953. retains the host launcher but not the MLIR dataflow needed to identify that
  1954. constant, so this must run while the freshly compiled function still owns
  1955. ``ir_module``. Any unrecognised lowering is reported as unknown rather
  1956. than guessed from the cubin's shared sections.
  1957. """
  1958. source = "cutlass-final-llvm-launch-config-field-2"
  1959. if not expected_kernels:
  1960. return {
  1961. "status": "unknown",
  1962. "source": source,
  1963. "reason": "kernel-info-unavailable",
  1964. "launch_dynamic_smem_bytes": {},
  1965. }
  1966. launches: dict[str, list[int]] = {kernel: [] for kernel in expected_kernels}
  1967. parsed_launches = 0
  1968. unparsed_launches = 0
  1969. for lines in _final_llvm_function_bodies(module_text):
  1970. if not any("llvm.call @_cudaLaunchKernelEx" in line for line in lines):
  1971. continue
  1972. constants: dict[str, int] = {}
  1973. geps: dict[str, tuple[str, tuple[str, ...]]] = {}
  1974. loads: dict[str, tuple[str, int]] = {}
  1975. stores: list[tuple[int, str, str]] = []
  1976. kernel_addresses: dict[str, str] = {}
  1977. def pointer_key(pointer: str) -> tuple[str, tuple[str, ...]]:
  1978. return geps.get(pointer, (pointer, ()))
  1979. def stored_value(pointer: str, before: int) -> str | None:
  1980. key = pointer_key(pointer)
  1981. for line_number, value, destination in reversed(stores):
  1982. if line_number < before and pointer_key(destination) == key:
  1983. return value
  1984. return None
  1985. for line_number, line in enumerate(lines):
  1986. if match := _LLVM_I64_CONSTANT_RE.match(line):
  1987. constants[match.group("result")] = int(match.group("value"))
  1988. continue
  1989. if match := _LLVM_GEP_RE.match(line):
  1990. indices = tuple(
  1991. item.strip() for item in match.group("indices").split(",")
  1992. )
  1993. geps[match.group("result")] = (match.group("base"), indices)
  1994. continue
  1995. if match := _LLVM_LOAD_RE.match(line):
  1996. loads[match.group("result")] = (
  1997. match.group("pointer"),
  1998. line_number,
  1999. )
  2000. continue
  2001. if match := _LLVM_STORE_RE.match(line):
  2002. stores.append(
  2003. (line_number, match.group("value"), match.group("pointer"))
  2004. )
  2005. continue
  2006. if match := _LLVM_KERNEL_ADDRESS_RE.match(line):
  2007. kernel_addresses[match.group("result")] = match.group("kernel")
  2008. continue
  2009. match = _LLVM_LAUNCH_EX_RE.search(line)
  2010. if match is None:
  2011. continue
  2012. arguments = [
  2013. argument.strip() for argument in match.group("arguments").split(",")
  2014. ]
  2015. if len(arguments) < 2:
  2016. unparsed_launches += 1
  2017. continue
  2018. config_operand, kernel_operand = arguments[:2]
  2019. kernel_load = loads.get(kernel_operand)
  2020. kernel = (
  2021. kernel_addresses.get(kernel_load[0])
  2022. if kernel_load is not None
  2023. else kernel_addresses.get(kernel_operand)
  2024. )
  2025. config = config_operand
  2026. if config_load := loads.get(config_operand):
  2027. config = stored_value(config_load[0], config_load[1]) or ""
  2028. smem_values: list[int] = []
  2029. if config:
  2030. for pointer, (base, indices) in geps.items():
  2031. if base != config or indices != ("0", "2"):
  2032. continue
  2033. value_name = stored_value(pointer, line_number)
  2034. if value_name in constants:
  2035. smem_values.append(constants[value_name])
  2036. if (
  2037. kernel not in expected_kernels
  2038. or len(smem_values) != 1
  2039. or smem_values[0] < 0
  2040. ):
  2041. unparsed_launches += 1
  2042. continue
  2043. launches[kernel].append(smem_values[0])
  2044. parsed_launches += 1
  2045. missing_kernels = sorted(
  2046. kernel for kernel, values in launches.items() if not values
  2047. )
  2048. if unparsed_launches or missing_kernels or parsed_launches == 0:
  2049. reason_parts = []
  2050. if unparsed_launches:
  2051. reason_parts.append(f"unparsed-launches={unparsed_launches}")
  2052. if missing_kernels:
  2053. reason_parts.append("missing-kernels=" + ",".join(missing_kernels))
  2054. if parsed_launches == 0 and not reason_parts:
  2055. reason_parts.append("no-launches")
  2056. return {
  2057. "status": "unknown",
  2058. "source": source,
  2059. "reason": ";".join(reason_parts),
  2060. "launch_dynamic_smem_bytes": {},
  2061. }
  2062. return {
  2063. "status": "exact",
  2064. "source": source,
  2065. "launch_dynamic_smem_bytes": {
  2066. kernel: values for kernel, values in sorted(launches.items())
  2067. },
  2068. }
  2069. def _extract_launch_dynamic_smem_bytes(compiled: Any) -> dict[str, Any]:
  2070. module = getattr(compiled, "ir_module", None)
  2071. kernel_info = getattr(compiled, "kernel_info", None)
  2072. source = "cutlass-final-llvm-launch-config-field-2"
  2073. if module is None:
  2074. return {
  2075. "status": "unknown",
  2076. "source": source,
  2077. "reason": "compiled-ir-unavailable",
  2078. "launch_dynamic_smem_bytes": {},
  2079. }
  2080. expected_kernels = (
  2081. {str(kernel) for kernel in kernel_info}
  2082. if isinstance(kernel_info, dict)
  2083. else set()
  2084. )
  2085. try:
  2086. return _parse_launch_dynamic_smem_from_final_llvm(str(module), expected_kernels)
  2087. except Exception as exc:
  2088. return {
  2089. "status": "unknown",
  2090. "source": source,
  2091. "reason": f"extractor-error:{type(exc).__name__}",
  2092. "launch_dynamic_smem_bytes": {},
  2093. }
  2094. def _build_compile_manifest(
  2095. cache_key: str,
  2096. cache_payload: tuple[object, ...],
  2097. func: Any,
  2098. object_bytes: bytes,
  2099. compiled: Any = None,
  2100. ) -> dict[str, Any]:
  2101. semantic_payload = _semantic_compile_manifest_payload(cache_payload)
  2102. semantic_json = json.dumps(
  2103. semantic_payload,
  2104. sort_keys=True,
  2105. separators=(",", ":"),
  2106. ensure_ascii=True,
  2107. allow_nan=False,
  2108. )
  2109. cache_format = str(cache_payload[0]) if cache_payload else "unknown"
  2110. explicit = cache_format == "b12x_cute_compile_cache_v6_explicit_spec"
  2111. options_index = 9 if explicit else 7
  2112. environment_index = 10 if explicit else 8
  2113. launch_metadata = (
  2114. _extract_launch_dynamic_smem_bytes(compiled)
  2115. if compiled is not None
  2116. else {
  2117. "status": "unknown",
  2118. "source": "cutlass-final-llvm-launch-config-field-2",
  2119. "reason": "compiled-ir-unavailable-on-object-reload",
  2120. "launch_dynamic_smem_bytes": {},
  2121. }
  2122. )
  2123. object_sha256 = hashlib.sha256(object_bytes).hexdigest()
  2124. artifact_evidence = {
  2125. "cache_key": cache_key,
  2126. "object_sha256": object_sha256,
  2127. "launch_metadata": launch_metadata,
  2128. }
  2129. artifact_evidence_json = json.dumps(
  2130. artifact_evidence,
  2131. sort_keys=True,
  2132. separators=(",", ":"),
  2133. ensure_ascii=True,
  2134. allow_nan=False,
  2135. )
  2136. manifest: dict[str, Any] = {
  2137. "schema": "b12x._lib.compile_manifest.v3",
  2138. "cache_key": cache_key,
  2139. "cache_format": cache_format,
  2140. "cache_payload_repr": repr(cache_payload),
  2141. "cache_payload": _manifest_json_value(cache_payload),
  2142. "object_sha256": object_sha256,
  2143. "object_bytes": len(object_bytes),
  2144. "semantic_key": hashlib.sha256(semantic_json.encode("utf-8")).hexdigest(),
  2145. "semantic_payload": semantic_payload,
  2146. "target": _compile_target_name(func),
  2147. "target_identity": _semantic_target_key(cache_payload[1]),
  2148. "package_fingerprint": str(cache_payload[2]),
  2149. "toolchain": _manifest_json_value(cache_payload[3]),
  2150. "compile_options": _manifest_json_value(cache_payload[options_index]),
  2151. "compile_environment": _manifest_json_value(cache_payload[environment_index]),
  2152. "launch_metadata": launch_metadata,
  2153. "artifact_evidence_sha256": hashlib.sha256(
  2154. artifact_evidence_json.encode("utf-8")
  2155. ).hexdigest(),
  2156. }
  2157. if explicit:
  2158. manifest["compile_spec_hash"] = str(cache_payload[5])
  2159. manifest["compile_spec_json"] = str(cache_payload[6])
  2160. manifest["compile_kwargs_hash"] = str(cache_payload[7])
  2161. manifest["compile_kwargs_json"] = str(cache_payload[8])
  2162. try:
  2163. spec = json.loads(str(cache_payload[6]))
  2164. except (TypeError, ValueError, json.JSONDecodeError):
  2165. spec = None
  2166. if isinstance(spec, dict):
  2167. manifest["kernel_id"] = str(spec.get("kernel", ""))
  2168. manifest["compile_spec_version"] = spec.get("version", "")
  2169. return manifest
  2170. def _write_compile_manifest(
  2171. cache_key: str,
  2172. cache_payload: tuple[object, ...],
  2173. func: Any,
  2174. object_bytes: bytes,
  2175. compiled: Any = None,
  2176. ) -> None:
  2177. manifest_path = _cache_manifest_path(cache_key)
  2178. manifest_path.parent.mkdir(parents=True, exist_ok=True)
  2179. manifest = _build_compile_manifest(
  2180. cache_key, cache_payload, func, object_bytes, compiled=compiled
  2181. )
  2182. tmp_name: str | None = None
  2183. try:
  2184. with tempfile.NamedTemporaryFile(
  2185. mode="w",
  2186. encoding="utf-8",
  2187. dir=manifest_path.parent,
  2188. prefix=f".{manifest_path.name}.",
  2189. suffix=".tmp",
  2190. delete=False,
  2191. ) as tmp_file:
  2192. tmp_name = tmp_file.name
  2193. json.dump(
  2194. manifest,
  2195. tmp_file,
  2196. sort_keys=True,
  2197. separators=(",", ":"),
  2198. ensure_ascii=True,
  2199. allow_nan=False,
  2200. )
  2201. tmp_file.write("\n")
  2202. os.replace(tmp_name, manifest_path)
  2203. tmp_name = None
  2204. finally:
  2205. if tmp_name is not None:
  2206. with suppress(OSError):
  2207. os.unlink(tmp_name)
  2208. def _ensure_cute_compile_manifest(
  2209. cache_key: str,
  2210. cache_payload: tuple[object, ...],
  2211. func: Any,
  2212. ) -> None:
  2213. manifest_path = _cache_manifest_path(cache_key)
  2214. if manifest_path.exists():
  2215. return
  2216. object_bytes = _cache_object_path(cache_key).read_bytes()
  2217. _write_compile_manifest(cache_key, cache_payload, func, object_bytes)
  2218. @contextmanager
  2219. def _disk_cache_key_lock(cache_key: str):
  2220. try:
  2221. import fcntl
  2222. except ImportError:
  2223. yield
  2224. return
  2225. lock_path = _cache_lock_path(cache_key)
  2226. lock_path.parent.mkdir(parents=True, exist_ok=True)
  2227. with open(lock_path, "w") as lock_file:
  2228. fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
  2229. try:
  2230. yield
  2231. finally:
  2232. fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
  2233. def _load_cute_compile_from_disk(cache_key: str):
  2234. from cutlass.base_dsl.export.external_binary_module import ExternalBinaryModule
  2235. object_path = _cache_object_path(cache_key)
  2236. if not object_path.exists():
  2237. return None
  2238. try:
  2239. # CUTLASS may finalize or patch the ELF while loading it. The cache
  2240. # object is content-addressed and its digest is recorded in the compile
  2241. # manifest, so never expose that canonical object to the loader.
  2242. with tempfile.TemporaryDirectory(
  2243. prefix="b12x-cute-cache-load-"
  2244. ) as raw_stage:
  2245. staged_object = Path(raw_stage) / object_path.name
  2246. shutil.copy2(object_path, staged_object)
  2247. module = ExternalBinaryModule(str(staged_object))
  2248. return getattr(module, _cache_prefix(cache_key))
  2249. except Exception:
  2250. return None
  2251. def _store_cute_compile_to_disk(
  2252. cache_key: str,
  2253. compiled: Any,
  2254. *,
  2255. cache_payload: tuple[object, ...] | None = None,
  2256. func: Any = None,
  2257. ) -> None:
  2258. if not hasattr(compiled, "dump_to_object"):
  2259. return
  2260. object_path = _cache_object_path(cache_key)
  2261. object_path.parent.mkdir(parents=True, exist_ok=True)
  2262. tmp_path = object_path.with_suffix(".tmp")
  2263. object_bytes = compiled.dump_to_object(_cache_prefix(cache_key))
  2264. with open(tmp_path, "wb") as f:
  2265. f.write(object_bytes)
  2266. os.replace(tmp_path, object_path)
  2267. if cache_payload is not None and func is not None:
  2268. _write_compile_manifest(
  2269. cache_key, cache_payload, func, object_bytes, compiled=compiled
  2270. )
  2271. def _memory_cache_get(cache_key: object) -> Any | None:
  2272. global _MEMORY_CACHE_HITS
  2273. global _MEMORY_CACHE_MISSES
  2274. if not _cute_compile_memory_cache_enabled():
  2275. return None
  2276. with _MEMORY_CACHE_LOCK:
  2277. compiled = _MEMORY_CACHE.get(cache_key)
  2278. if compiled is None:
  2279. _MEMORY_CACHE_MISSES += 1
  2280. return None
  2281. _MEMORY_CACHE_HITS += 1
  2282. _MEMORY_CACHE.move_to_end(cache_key)
  2283. return compiled
  2284. def _memory_cache_put(cache_key: object, compiled: Any) -> None:
  2285. if not _cute_compile_memory_cache_enabled():
  2286. return
  2287. with _MEMORY_CACHE_LOCK:
  2288. _MEMORY_CACHE[cache_key] = compiled
  2289. _MEMORY_CACHE.move_to_end(cache_key)
  2290. while len(_MEMORY_CACHE) > _cute_compile_memory_cache_size():
  2291. _MEMORY_CACHE.popitem(last=False)
  2292. def clear_compile_cache() -> None:
  2293. global _MEMORY_CACHE_HITS
  2294. global _MEMORY_CACHE_MISSES
  2295. global _DISK_CACHE_HITS
  2296. global _COMPILE_MISSES
  2297. global _SPEC_MEMO_HITS
  2298. global _SPEC_MEMO_MISSES
  2299. global _COMPILE_PROGRESS_COUNT
  2300. global _COMPILE_PROGRESS_TOTAL_SECONDS
  2301. _compile_environment_key.cache_clear()
  2302. _static_compile_cache_context.cache_clear()
  2303. _DEVICE_UUID_KEYS.clear()
  2304. _DEVICE_COMPILE_CACHE_CONTEXTS.clear()
  2305. with _MEMORY_CACHE_LOCK:
  2306. _MEMORY_CACHE.clear()
  2307. _MEMORY_CACHE_HITS = 0
  2308. _MEMORY_CACHE_MISSES = 0
  2309. _DISK_CACHE_HITS = 0
  2310. _COMPILE_MISSES = 0
  2311. with _SPEC_MEMO_LOCK:
  2312. _SPEC_MEMO.clear()
  2313. _SPEC_MEMO_HITS = 0
  2314. _SPEC_MEMO_MISSES = 0
  2315. with _COMPILE_PROGRESS_LOCK:
  2316. _COMPILE_PROGRESS_COUNT = 0
  2317. _COMPILE_PROGRESS_TOTAL_SECONDS = 0.0
  2318. def compile_cache_info() -> dict[str, int | bool]:
  2319. with _MEMORY_CACHE_LOCK:
  2320. info: dict[str, int | bool] = {
  2321. "memory_cache_enabled": _cute_compile_memory_cache_enabled(),
  2322. "memory_cache_size": len(_MEMORY_CACHE),
  2323. "memory_cache_max_size": _cute_compile_memory_cache_size(),
  2324. "memory_cache_hits": _MEMORY_CACHE_HITS,
  2325. "memory_cache_misses": _MEMORY_CACHE_MISSES,
  2326. "disk_cache_enabled": _cute_compile_disk_cache_enabled(),
  2327. "disk_cache_hits": _DISK_CACHE_HITS,
  2328. "compile_misses": _COMPILE_MISSES,
  2329. }
  2330. with _SPEC_MEMO_LOCK:
  2331. info["spec_memo_enabled"] = _spec_memo_enabled()
  2332. info["spec_memo_size"] = len(_SPEC_MEMO)
  2333. info["spec_memo_hits"] = _SPEC_MEMO_HITS
  2334. info["spec_memo_misses"] = _SPEC_MEMO_MISSES
  2335. return info
  2336. def compile(
  2337. func: Any,
  2338. *args: Any,
  2339. compile_spec: KernelCompileSpec | None = None,
  2340. dsl_compile_options: Any = None,
  2341. **kwargs: Any,
  2342. ) -> Any:
  2343. import cutlass.cute as cute
  2344. global _DISK_CACHE_HITS
  2345. global _COMPILE_MISSES
  2346. compile_callable = cute.compile
  2347. if dsl_compile_options is not None:
  2348. # Subscript-style DSL compile options (e.g. OptLevel(2): ptxas -O3's
  2349. # scheduler register-starves some scalar-heavy kernels; see the w4a8
  2350. # dynamic MoE recipe).
  2351. if hasattr(compile_callable, "__getitem__"):
  2352. compile_callable = compile_callable[dsl_compile_options]
  2353. else:
  2354. # Some embedded runtimes expose cutlass.cute.compile as a plain
  2355. # function instead of the CompileCallable instance installed by the
  2356. # top-level module import. Recreate the callable explicitly so DSL
  2357. # options still take effect instead of crashing or silently falling
  2358. # back to the default compiler options.
  2359. from cutlass.base_dsl.compiler import CompileCallable
  2360. compile_callable = CompileCallable(dsl_compile_options)
  2361. kwargs = dict(kwargs)
  2362. kwargs["__dsl_compile_options_key"] = _structural_cache_key(dsl_compile_options)
  2363. memory_cache_key = _compile_memory_cache_key(
  2364. compile_callable, func, args, kwargs, compile_spec
  2365. )
  2366. compiled = _memory_cache_get(memory_cache_key)
  2367. if compiled is not None:
  2368. return compiled
  2369. from b12x._lib.runtime_control import (
  2370. raise_if_kernel_resolution_frozen,
  2371. )
  2372. raise_if_kernel_resolution_frozen(
  2373. "cute.compile",
  2374. target=func,
  2375. cache_key=compile_spec if compile_spec is not None else memory_cache_key,
  2376. )
  2377. post_engine_start_log = _cute_compile_post_engine_start_log_enabled()
  2378. payload = _compile_disk_cache_payload(
  2379. compile_callable, func, args, kwargs, compile_spec
  2380. )
  2381. cache_key = hashlib.sha256(repr(payload).encode("utf-8")).hexdigest()
  2382. disk_cache_enabled = _cute_compile_disk_cache_enabled_for_payload(payload)
  2383. if disk_cache_enabled:
  2384. compiled = _load_cute_compile_from_disk(cache_key)
  2385. if compiled is not None:
  2386. with suppress(Exception):
  2387. _ensure_cute_compile_manifest(cache_key, payload, func)
  2388. with _MEMORY_CACHE_LOCK:
  2389. _DISK_CACHE_HITS += 1
  2390. if post_engine_start_log:
  2391. with suppress(Exception):
  2392. _log_cute_compile_event(
  2393. func,
  2394. args,
  2395. kwargs,
  2396. event="disk-hit",
  2397. cache_status="disk-cache-hit",
  2398. cache_payload=payload,
  2399. reason="post-engine-start",
  2400. cache_key=cache_key,
  2401. )
  2402. _memory_cache_put(memory_cache_key, compiled)
  2403. return compiled
  2404. with _disk_cache_key_lock(cache_key):
  2405. compiled = _memory_cache_get(memory_cache_key)
  2406. if compiled is not None:
  2407. return compiled
  2408. compiled = _load_cute_compile_from_disk(cache_key)
  2409. if compiled is not None:
  2410. with suppress(Exception):
  2411. _ensure_cute_compile_manifest(cache_key, payload, func)
  2412. with _MEMORY_CACHE_LOCK:
  2413. _DISK_CACHE_HITS += 1
  2414. if post_engine_start_log:
  2415. with suppress(Exception):
  2416. _log_cute_compile_event(
  2417. func,
  2418. args,
  2419. kwargs,
  2420. event="disk-hit-after-wait",
  2421. cache_status="disk-cache-hit-after-wait",
  2422. cache_payload=payload,
  2423. reason="post-engine-start",
  2424. cache_key=cache_key,
  2425. )
  2426. _memory_cache_put(memory_cache_key, compiled)
  2427. return compiled
  2428. cache_status = "disk-cache-miss"
  2429. if _cute_compile_log_enabled() or post_engine_start_log:
  2430. with suppress(Exception):
  2431. _log_cute_compile_miss(
  2432. func,
  2433. args,
  2434. kwargs,
  2435. cache_status=cache_status,
  2436. cache_payload=payload,
  2437. reason="post-engine-start" if post_engine_start_log else None,
  2438. cache_key=cache_key,
  2439. )
  2440. with _MEMORY_CACHE_LOCK:
  2441. _COMPILE_MISSES += 1
  2442. call_kwargs = {
  2443. k: v for k, v in kwargs.items() if k != "__dsl_compile_options_key"
  2444. }
  2445. compiled = _call_cute_compile(
  2446. compile_callable,
  2447. func,
  2448. args,
  2449. call_kwargs,
  2450. compile_spec=compile_spec,
  2451. cache_key=cache_key,
  2452. )
  2453. with suppress(Exception):
  2454. _store_cute_compile_to_disk(
  2455. cache_key,
  2456. compiled,
  2457. cache_payload=payload,
  2458. func=func,
  2459. )
  2460. _memory_cache_put(memory_cache_key, compiled)
  2461. return compiled
  2462. else:
  2463. cache_status = (
  2464. "disk-cache-device-uuid-unavailable"
  2465. if _cute_compile_disk_cache_enabled()
  2466. else "disk-cache-disabled"
  2467. )
  2468. if _cute_compile_log_enabled() or post_engine_start_log:
  2469. with suppress(Exception):
  2470. _log_cute_compile_miss(
  2471. func,
  2472. args,
  2473. kwargs,
  2474. cache_status=cache_status,
  2475. cache_payload=payload,
  2476. reason="post-engine-start" if post_engine_start_log else None,
  2477. cache_key=cache_key,
  2478. )
  2479. with _MEMORY_CACHE_LOCK:
  2480. _COMPILE_MISSES += 1
  2481. call_kwargs = {k: v for k, v in kwargs.items() if k != "__dsl_compile_options_key"}
  2482. compiled = _call_cute_compile(
  2483. compile_callable,
  2484. func,
  2485. args,
  2486. call_kwargs,
  2487. compile_spec=compile_spec,
  2488. cache_key=cache_key,
  2489. )
  2490. if disk_cache_enabled:
  2491. with suppress(Exception):
  2492. _store_cute_compile_to_disk(
  2493. cache_key,
  2494. compiled,
  2495. cache_payload=payload,
  2496. func=func,
  2497. )
  2498. _memory_cache_put(memory_cache_key, compiled)
  2499. return compiled
  2500. def _cached_default_executor(compiled: Any) -> Any | None:
  2501. if not hasattr(compiled, "_default_executor"):
  2502. return None
  2503. executor = getattr(compiled, _EXECUTOR_CACHE_ATTR, None)
  2504. if executor is not None:
  2505. return executor
  2506. with _EXECUTOR_CACHE_LOCK:
  2507. executor = getattr(compiled, _EXECUTOR_CACHE_ATTR, None)
  2508. if executor is not None:
  2509. return executor
  2510. executor = getattr(compiled, "_default_executor", None)
  2511. if executor is None:
  2512. to_executor = getattr(compiled, "to", None)
  2513. if to_executor is None:
  2514. return None
  2515. executor = to_executor(None)
  2516. with suppress(Exception):
  2517. compiled._default_executor = executor
  2518. with suppress(Exception):
  2519. setattr(compiled, _EXECUTOR_CACHE_ATTR, executor)
  2520. return executor
  2521. def run_compiled(compiled: Any, args: tuple[Any, ...]) -> Any:
  2522. if hasattr(compiled, "generate_execution_args") and hasattr(
  2523. compiled, "run_compiled_program"
  2524. ):
  2525. execution_args, _ = compiled.generate_execution_args(*args)
  2526. executor = _cached_default_executor(compiled)
  2527. if executor is not None and hasattr(executor, "run_compiled_program"):
  2528. return executor.run_compiled_program(execution_args)
  2529. return compiled.run_compiled_program(execution_args)
  2530. return compiled(*args)
  2531. def launch(
  2532. func: Any,
  2533. *,
  2534. compile_spec: KernelCompileSpec,
  2535. compile_args: tuple[Any, ...],
  2536. runtime_args: tuple[Any, ...],
  2537. compile_kwargs: dict[str, Any] | None = None,
  2538. ) -> Any:
  2539. compiled = compile(
  2540. func,
  2541. *compile_args,
  2542. compile_spec=compile_spec,
  2543. **(compile_kwargs or {}),
  2544. )
  2545. return run_compiled(compiled, runtime_args)