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.
- sparse_delta_memory/__init__.py +13 -0
- sparse_delta_memory/backward.py +367 -0
- sparse_delta_memory/cache.py +70 -0
- sparse_delta_memory/cuda/__init__.py +3 -0
- sparse_delta_memory/cuda/_build.py +254 -0
- sparse_delta_memory/cuda/_device.py +158 -0
- sparse_delta_memory/cuda/_devmemo.py +64 -0
- sparse_delta_memory/cuda/_image.py +128 -0
- sparse_delta_memory/cuda/bwd_steps_cuda.py +111 -0
- sparse_delta_memory/cuda/gather_cuda.py +796 -0
- sparse_delta_memory/cuda/gather_ns_cuda.py +134 -0
- sparse_delta_memory/cuda/gx_gds_cuda.py +1297 -0
- sparse_delta_memory/cuda/gx_gemm_cuda.py +857 -0
- sparse_delta_memory/cuda/gx_pa2_cuda.py +1320 -0
- sparse_delta_memory/cuda/mover_da2_cuda.py +1802 -0
- sparse_delta_memory/cuda/mover_fx_cuda.py +2229 -0
- sparse_delta_memory/cuda/pa_dc_cuda.py +1500 -0
- sparse_delta_memory/cuda/phase1_bwd_cuda.py +852 -0
- sparse_delta_memory/cuda/phase1_common.cuh +181 -0
- sparse_delta_memory/cuda/phase1_cuda.py +3801 -0
- sparse_delta_memory/cuda/product_key_topk_anyf_cuda.py +668 -0
- sparse_delta_memory/cuda/product_key_topk_cuda.py +735 -0
- sparse_delta_memory/cuda/px_qrec_cuda.py +167 -0
- sparse_delta_memory/cuda/sdm_copy.cuh +90 -0
- sparse_delta_memory/cuda/sdm_plancap.cuh +150 -0
- sparse_delta_memory/cuda_graph.py +605 -0
- sparse_delta_memory/decode.py +140 -0
- sparse_delta_memory/forward.py +464 -0
- sparse_delta_memory/layer.py +663 -0
- sparse_delta_memory/memory_ops.py +1069 -0
- sparse_delta_memory/memory_stats.py +221 -0
- sparse_delta_memory/movers.py +198 -0
- sparse_delta_memory/phase2_kernels.py +267 -0
- sparse_delta_memory/product_key_topk.py +113 -0
- sparse_delta_memory/wy_solve.py +273 -0
- sparse_delta_memory-2.0.0.dist-info/METADATA +482 -0
- sparse_delta_memory-2.0.0.dist-info/RECORD +40 -0
- sparse_delta_memory-2.0.0.dist-info/WHEEL +5 -0
- sparse_delta_memory-2.0.0.dist-info/licenses/LICENSE +407 -0
- 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)
|