sparse-delta-memory 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. sparse_delta_memory/__init__.py +13 -0
  2. sparse_delta_memory/backward.py +367 -0
  3. sparse_delta_memory/cache.py +70 -0
  4. sparse_delta_memory/cuda/__init__.py +3 -0
  5. sparse_delta_memory/cuda/_build.py +254 -0
  6. sparse_delta_memory/cuda/_device.py +158 -0
  7. sparse_delta_memory/cuda/_devmemo.py +64 -0
  8. sparse_delta_memory/cuda/_image.py +128 -0
  9. sparse_delta_memory/cuda/bwd_steps_cuda.py +111 -0
  10. sparse_delta_memory/cuda/gather_cuda.py +796 -0
  11. sparse_delta_memory/cuda/gather_ns_cuda.py +134 -0
  12. sparse_delta_memory/cuda/gx_gds_cuda.py +1297 -0
  13. sparse_delta_memory/cuda/gx_gemm_cuda.py +857 -0
  14. sparse_delta_memory/cuda/gx_pa2_cuda.py +1320 -0
  15. sparse_delta_memory/cuda/mover_da2_cuda.py +1802 -0
  16. sparse_delta_memory/cuda/mover_fx_cuda.py +2229 -0
  17. sparse_delta_memory/cuda/pa_dc_cuda.py +1500 -0
  18. sparse_delta_memory/cuda/phase1_bwd_cuda.py +852 -0
  19. sparse_delta_memory/cuda/phase1_common.cuh +181 -0
  20. sparse_delta_memory/cuda/phase1_cuda.py +3801 -0
  21. sparse_delta_memory/cuda/product_key_topk_anyf_cuda.py +668 -0
  22. sparse_delta_memory/cuda/product_key_topk_cuda.py +735 -0
  23. sparse_delta_memory/cuda/px_qrec_cuda.py +167 -0
  24. sparse_delta_memory/cuda/sdm_copy.cuh +90 -0
  25. sparse_delta_memory/cuda/sdm_plancap.cuh +150 -0
  26. sparse_delta_memory/cuda_graph.py +605 -0
  27. sparse_delta_memory/decode.py +140 -0
  28. sparse_delta_memory/forward.py +464 -0
  29. sparse_delta_memory/layer.py +663 -0
  30. sparse_delta_memory/memory_ops.py +1069 -0
  31. sparse_delta_memory/memory_stats.py +221 -0
  32. sparse_delta_memory/movers.py +198 -0
  33. sparse_delta_memory/phase2_kernels.py +267 -0
  34. sparse_delta_memory/product_key_topk.py +113 -0
  35. sparse_delta_memory/wy_solve.py +273 -0
  36. sparse_delta_memory-2.0.0.dist-info/METADATA +482 -0
  37. sparse_delta_memory-2.0.0.dist-info/RECORD +40 -0
  38. sparse_delta_memory-2.0.0.dist-info/WHEEL +5 -0
  39. sparse_delta_memory-2.0.0.dist-info/licenses/LICENSE +407 -0
  40. sparse_delta_memory-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,134 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ """gather_ns_cuda: the no-stash token gather of the deterministic forward ("ns": no stash), built from gather_cuda's
3
+ source with GATHER_NS defined.
4
+
5
+ Compared with token_gather it skips the per-entry stash (or, given ``snapc`` and ``kpos``, stores the raw write rows of
6
+ the valid group FIRSTs into the compact snapshot), loads an entry batch's kpos before its slots and weights, can run as
7
+ a programmatic dependent launch, and offers a plan / step entry pair for the step loop. Every accumulation (retrieved,
8
+ inter, d_delta_v) is token_gather's code, so the outputs are bit-identical to token_gather with the same geometry."""
9
+
10
+ import functools
11
+
12
+ import torch
13
+
14
+ from . import gather_cuda
15
+ from ._build import load_cuda_extension
16
+ from ._device import l2_bytes
17
+
18
+ # Variants of gather_cuda's source selected by GATHER_NS:
19
+ # - Entry loads: an entry batch's kpos load is issued before its slot / weight loads, so the first slot broadcast does
20
+ # not wait behind it. The same loads of the same values.
21
+ # - Programmatic dependent launch: the deterministic forward launches each step's gather (steps >= 1) right after the
22
+ # previous step's DW, which triggers its dependents once its own wait has returned. The gather's slot / weight / kpos
23
+ # loads (Phase 1 outputs and staged indices, written before the step loop) run before griddepcontrol.wait; every bank
24
+ # row is read after it, through the coherent L2 path (__ldcg: the DW has just written them), and no store precedes it.
25
+ # Right after its wait the grid lets its own dependents launch (griddepcontrol.launch_dependents; a no-op unless the
26
+ # next launch is programmatic). Arithmetic and order are unchanged.
27
+ # - Plan / step: token_gather_dc_plan runs token_gather_dc's checks and argument set-up once (a dry run whose launch()
28
+ # records the kernel instantiation, Args, grid, block and shared memory instead of launching); token_gather_dc_step(
29
+ # plan, row0, valid, pdl) then launches that kernel with only row0 / valid changed. Same kernel, same arguments.
30
+ # - L2 hints (GATHER_L2 also defined): the step's write-slot rows (operand 0) should stay in L2 for the DW that follows
31
+ # (it reads each written group's base row bank[slot] right after this grid), so the read-slot rows (operand 1,
32
+ # read-only in the step) are loaded with an L2 evict-first policy, and so are the compact snapshot rows stored (read
33
+ # by the backward much later). Cache policy only: the same loads and stores of the same values. 16-byte vectors
34
+ # (uint4) take the hinted instructions (sm_80+); other vector types and older architectures keep the plain ones. The
35
+ # hinted build is used only when l2_hint (below) finds the step's reads sparse enough.
36
+
37
+
38
+ def _source(l2=False):
39
+ """The no-stash gather source: gather_cuda's single source with GATHER_NS defined (the no-stash stores and checks,
40
+ the kpos-first entry loads, the PDL hooks, the plan entry points) and, for l2, GATHER_L2 (the L2 hints)."""
41
+ return "#define GATHER_NS 1\n" + ("#define GATHER_L2 1\n" if l2 else "") + gather_cuda._CUDA_SOURCE
42
+
43
+
44
+ def _cpp():
45
+ return "#define GATHER_NS 1\n" + gather_cuda._CPP_SOURCE
46
+
47
+
48
+ L2_FETCH = [0] # number of _module(l2=True) calls (the L2-hinted build returned)
49
+ # The read-row evict-first hint costs in-step L2 reuse when a step's reads overflow L2 and helps sparse, small steps.
50
+ # Shape rule: the step's read density R * (tokens per step) / (bank rows) is at most _L2_DMAX (num, den), and its
51
+ # read-entry bytes R * (tokens per step) * row_bytes are at most _l2_emax(device).
52
+ _L2_DMAX = (1, 10)
53
+ # The read-entry bound scales with the device's L2: 176 MiB on GB300, whose L2 is 135528448 bytes (exact there).
54
+ _L2_EMAX_GB300 = 176 << 20
55
+ _L2_GB300 = 135528448
56
+
57
+
58
+ def _l2_emax(device=None) -> int:
59
+ return _L2_EMAX_GB300 * l2_bytes(device) // _L2_GB300
60
+
61
+
62
+ def l2_hint(read_width, step_tokens, bank_rows, row_bytes, device=None):
63
+ """True iff the step's read density read_width * step_tokens / bank_rows is at most _L2_DMAX and its read-entry bytes
64
+ read_width * step_tokens * row_bytes are at most _l2_emax(device) (device: the bank's, default the current device).
65
+ Shapes and L2 size only; the deterministic forward calls it once per call with its tokens per step. Python ints: no
66
+ overflow."""
67
+ num, den = _L2_DMAX
68
+ reads = read_width * step_tokens
69
+ ok = (read_width > 0 and step_tokens > 0 and bank_rows > 0 and row_bytes > 0
70
+ and reads * den <= num * bank_rows and reads * row_bytes <= _l2_emax(device))
71
+ return ok
72
+
73
+
74
+ def _module(l2=False):
75
+ """The module for the caller's L2 decision l2 (True: the L2-hinted source, chosen by the deterministic forward with
76
+ l2_hint; False: the unhinted source). One cached build per l2. _module.cache_clear() clears every build (the
77
+ functools.cache API callers may rely on)."""
78
+ m = _build(bool(l2))
79
+ if l2:
80
+ L2_FETCH[0] += 1
81
+ return m
82
+
83
+
84
+ @functools.cache
85
+ def _build(l2):
86
+ return load_cuda_extension(
87
+ "sdm_token_gather_dc",
88
+ cpp_sources=_cpp(),
89
+ cuda_sources=_source(l2),
90
+ functions=("token_gather_dc", "token_gather_dc_plan", "token_gather_dc_step", "token_gather_dc_launch_addr"),
91
+ extra_cuda_cflags=("-O3",),
92
+ )
93
+
94
+
95
+ _module.cache_clear = _build.cache_clear
96
+
97
+
98
+ def pdl_ok(device):
99
+ """PDL may be requested for launches on `device` iff it is sm_90+ (the hooks compile away below sm_90, so a
100
+ programmatic launch there could overlap its predecessor unguarded: never request it)."""
101
+ from .mover_fx_cuda import fwd_pdl_ok
102
+ return fwd_pdl_ok(device)
103
+
104
+
105
+ def token_gather_ns(bank, idx, weights, out, geometry, *, q_idx=None, q_weights=None, q_out=None,
106
+ tokens, chunk, valid, row0=0, out_row0=0, snapc=None, kpos=None, crow=0, pdl=False, l2=False):
107
+ """token_gather without the per-entry stash (outputs bit-identical to token_gather's). With ``snapc`` and ``kpos`` it
108
+ stores the gathered raw write rows of the valid group FIRSTs into the compact snapshot instead: row
109
+ (chunk * crow + kpos[entry]) for kpos >= 0. pdl: a programmatic dependent launch; the caller asserts that the launch
110
+ right before this one wrote none of the slots, weights or kpos (the deterministic forward: the previous step's DW).
111
+ l2: the caller's l2_hint decision."""
112
+ if snapc is None:
113
+ snapc = torch.empty(0, device=bank.device, dtype=bank.dtype)
114
+ kpos = None
115
+ _module(l2).token_gather_dc(bank, idx, weights, snapc, out, q_idx, q_weights, q_out, row0,
116
+ row0 if kpos is not None else 0, out_row0, tokens, chunk, valid,
117
+ geometry.vec_bytes, geometry.lanes, geometry.groups, geometry.tpc,
118
+ kpos, crow, 1 if pdl else 0)
119
+
120
+ def tail_supported(bank, chunk_size, quant="none") -> bool:
121
+ """movers.tail_admits hook (native tails): this gather runs the call's tail class
122
+ (rows that are not 16-byte multiples take element-sized vectors, see gather_cuda.gather_geometry: arch-neutral code)
123
+ on every backend of _device.backend (modern, a100; rtx with chunks of <= 128 tokens, rule 0e), so a tail class takes
124
+ the same route everywhere. Unquantized snapshots only."""
125
+ import torch as _t
126
+ # snapshot_quant bf16 (float32 banks): the DW writes the compact rows; this gather is the unquantized one
127
+ if quant not in ("none", "bf16") or not bank.is_cuda or bank.dtype not in (_t.bfloat16, _t.float32):
128
+ return False
129
+ from ._device import backend
130
+ try:
131
+ b = backend(bank.device)
132
+ except RuntimeError:
133
+ return False
134
+ return b in ("modern", "a100") or (b == "rtx" and chunk_size <= 128)