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,482 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sparse-delta-memory
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Sparse Delta Memory (SDM): a sparse memory layer for language models, with deterministic CUDA kernels
|
|
5
|
+
License-Expression: CC-BY-NC-4.0
|
|
6
|
+
Project-URL: Repository, https://github.com/facebookresearch/sparse-delta-memory
|
|
7
|
+
Project-URL: Changelog, https://github.com/facebookresearch/sparse-delta-memory/blob/main/CHANGELOG.md
|
|
8
|
+
Keywords: sparse memory,language models,sequence mixer,CUDA,deterministic
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: torch==2.13.0
|
|
20
|
+
Requires-Dist: triton==3.7.1
|
|
21
|
+
Requires-Dist: ninja
|
|
22
|
+
Requires-Dist: einops
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# Sparse Delta Memory: layer and kernels
|
|
26
|
+
|
|
27
|
+
`SparseDeltaMemory` (`layer.py`) is a sequence mixer over a per-head memory bank of
|
|
28
|
+
`slots_per_head` rows, addressed by product keys and updated with the gated delta rule.
|
|
29
|
+
This file describes the implementation; the repository's README
|
|
30
|
+
(https://github.com/facebookresearch/sparse-delta-memory) covers the model, the training code and
|
|
31
|
+
the results.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install torch==2.13.0 --index-url https://download.pytorch.org/whl/cu130 # first, the tested CUDA build of PyTorch
|
|
37
|
+
pip install sparse-delta-memory
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import torch
|
|
42
|
+
from sparse_delta_memory import SparseDeltaMemory, SparseDeltaMemoryArgs
|
|
43
|
+
|
|
44
|
+
args = SparseDeltaMemoryArgs(dim=2048, num_heads=2, slots_per_head=65536,
|
|
45
|
+
num_writes=64, num_reads=64, chunk_size=64)
|
|
46
|
+
layer = SparseDeltaMemory(args, layer_id=0).cuda()
|
|
47
|
+
layer.init_weights()
|
|
48
|
+
layer = layer.to(torch.bfloat16)
|
|
49
|
+
x = torch.randn(2, 4096, 2048, device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
|
50
|
+
y, _ = layer(x) # [batch, tokens, dim]; the bank advances in place
|
|
51
|
+
y.float().square().mean().backward() # deterministic kernels (see Determinism)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Linux and an NVIDIA GPU (tested on GB300 and H100), Python >= 3.12. The CUDA extensions compile on
|
|
55
|
+
first use and need a CUDA toolkit matching `torch.version.cuda` and a host C++ compiler (see Build
|
|
56
|
+
requirements). License: CC-BY-NC 4.0.
|
|
57
|
+
|
|
58
|
+
## What the layer computes
|
|
59
|
+
|
|
60
|
+
Per token `t` and head, the key and query projections give the scores of the product-key
|
|
61
|
+
factors: by default two factors of `side = sqrt(slots_per_head)` entries each, or any number
|
|
62
|
+
`F >= 1` of factors with the widths `n_0, ..., n_{F-1}` of `product_key_factor_dims` (their
|
|
63
|
+
product is `slots_per_head`, and each projection has `sum(n_f)` columns per head). The exact
|
|
64
|
+
top-k over all sums of one score from each factor selects `num_writes` write slots
|
|
65
|
+
`(k_idx, k_val)` and `num_reads` read slots `(q_idx, q_val)`, weighted by `write_act` /
|
|
66
|
+
`read_act`; slot `(c_0, ..., c_{F-1})` is bank row `(..(c_0 * n_1 + c_1) * ..) * n_{F-1} +
|
|
67
|
+
c_{F-1}` (`a * side + b` for the two default factors). With `g_t = -exp(A_log) * softplus(a_proj(x_t) + dt_bias)` and
|
|
68
|
+
`beta_t = sigmoid(b_proj(x_t))`, only the touched slots change:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
M[s] <- exp(g_t) * M[s] for s in k_idx_t
|
|
72
|
+
r_t = sum_n k_val[n] * M[k_idx[n]]
|
|
73
|
+
M[s_n] <- M[s_n] + k_val[n] * beta_t * (v_t - r_t)
|
|
74
|
+
y_t = sum_m q_val[m] * M[q_idx[m]]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`y` then goes through an optional per-head LayerNorm (`normalize_readings`), an optional
|
|
78
|
+
sigmoid output gate and `Wo`. With `key_weighted_decay` the decay is `exp(g_t * k_val[n])`
|
|
79
|
+
per write; with `backprop_on_memory` the initial bank is a learned parameter.
|
|
80
|
+
|
|
81
|
+
The op (`memory_ops.sdm_write_read`) runs the recurrence in chunks of
|
|
82
|
+
`C = chunk_size` tokens:
|
|
83
|
+
|
|
84
|
+
- **Phase 1** (all chunks at once, independent of the bank): the write and read entries of
|
|
85
|
+
each chunk are sorted by slot; segmented scans give the per-slot log-decays, and walks
|
|
86
|
+
inside each slot group build the decay-weighted Gram matrices `A` (writes x writes,
|
|
87
|
+
strictly causal) and `QK` (reads x writes, causal).
|
|
88
|
+
- **Solve**: `X = (I + tril(beta * A, -1))^-1` and `QX = QK @ X` per chunk.
|
|
89
|
+
- **Phase 2** (sequential over chunks, the `B * num_heads` streams side by side): a gather
|
|
90
|
+
of bank rows, one dual matmul producing the corrected writes and the readings, and a
|
|
91
|
+
decay-and-write into the bank. The backward walks the chunks in reverse.
|
|
92
|
+
|
|
93
|
+
The chunked form is algebraically equivalent to the token loop, so `chunk_size`
|
|
94
|
+
changes the output only through floating-point rounding: the order of accumulation and,
|
|
95
|
+
with a bfloat16 bank, the points where bank rows are rounded. A sequence need not fill its
|
|
96
|
+
last chunk: tokens past the end of a stream are masked out. Every sum runs in a fixed order
|
|
97
|
+
and every bank row has one writer per launch, so results are bitwise reproducible run to run
|
|
98
|
+
(see Determinism).
|
|
99
|
+
|
|
100
|
+
## Files
|
|
101
|
+
|
|
102
|
+
`layer.py` (the layer), `memory_ops.py` (the op, `sdm_write_read`), `decode.py` (the single-token
|
|
103
|
+
step, `sdm_decode_step`, no GPU check) and `cuda/` (the CUDA kernels); the repository README lists
|
|
104
|
+
every module.
|
|
105
|
+
|
|
106
|
+
## Execution
|
|
107
|
+
|
|
108
|
+
All supported shapes run the same recurrence and source kernels (compile-time specializations; the
|
|
109
|
+
launch geometry and a few paths vary with the shape). Newer kernels serve the calls they admit,
|
|
110
|
+
chosen by shape, dtype and GPU architecture only (Limits, in Determinism); the others run
|
|
111
|
+
another deterministic kernel of the same step, with the same guarantees. The layer routes each call
|
|
112
|
+
on whether it may need a backward (grad mode is on and an input requires grad), in train and eval
|
|
113
|
+
mode alike:
|
|
114
|
+
|
|
115
|
+
| Call | Path |
|
|
116
|
+
|---|---|
|
|
117
|
+
| may need a backward | the chunked op, saving what its backward reads |
|
|
118
|
+
| no backward, T > 1 | the chunked op without saved state: the snapshot and `delta_v` are per-step scratch and the chunks run in bounded slabs, so working memory does not grow with T |
|
|
119
|
+
| no backward, T == 1 | one decode launch (write and read of all streams); its arguments are checked only when the bank's layout changes |
|
|
120
|
+
|
|
121
|
+
Run inference under `torch.no_grad()` or `torch.inference_mode()`. An eval-mode call under
|
|
122
|
+
grad computes the training gradients.
|
|
123
|
+
|
|
124
|
+
Kernels of the op. Forward: a staging copy to time-major chunks (skipped when the input already has
|
|
125
|
+
that layout), Phase 1, the solve, then per chunk step the token gather, the dual matmul and the
|
|
126
|
+
decay-and-write (which also quantizes the snapshot). Backward: per chunk step, in reverse, the
|
|
127
|
+
gather of the bank gradient, one `baddbmm`, the write-side mover and the read-side mover; then the
|
|
128
|
+
solve and Phase-1 backward. Steps of `2**31` entries or more use 64-bit entry indices in the write
|
|
129
|
+
mover, and compact snapshots of `2**31` rows or more 64-bit row offsets in the backward.
|
|
130
|
+
|
|
131
|
+
`memory_ops.last_dispatch()` returns the launch decisions of the latest op call (updated on eager
|
|
132
|
+
calls and captures, never by graph replays; it reads no tensor values).
|
|
133
|
+
|
|
134
|
+
## Supported inputs and limits
|
|
135
|
+
|
|
136
|
+
No fallback paths: one native kernel family per supported GPU class, and an explicit error otherwise.
|
|
137
|
+
|
|
138
|
+
| | Supported | Otherwise |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `num_writes`, `num_reads` | 1..256, and at most `slots_per_head` | `ValueError` at construction |
|
|
141
|
+
| `chunk_size` | `None` (the op's default, 64) or any integer in 16..256; at most 128 on RTX GPUs | `ValueError` at construction (on RTX GPUs, above 128: `ValueError` in the forward, before the op does any work) |
|
|
142
|
+
| `slots_per_head` | a perfect square, `side <= 65535`; with `product_key_factor_dims`, their product | `ValueError` at construction |
|
|
143
|
+
| `product_key_factor_dims` | `None`, or 1 to 512 positive ints (at most `2**31 - 1` columns in total) | `ValueError` at construction |
|
|
144
|
+
| `snapshot_quant` | `none`, `bf16`, `fp8`, `int4`; it only affects what training saves for the backward: inference (calls without a backward) is identical to `snapshot_quant="none"` | `ValueError` at construction |
|
|
145
|
+
| `snapshot_quant` `fp8` / `int4` on a call that may need a backward | head_dim <= 16384 that splits into `ceil(head_dim / 128)` equal column slices whose width is a multiple of 8 (bfloat16 bank) or of 4 (float32 bank), e.g. 64, 128, 480, 960, 1024, 2048; int4 snapshots on a float32 bank need slices whose width is a multiple of 8: slices 4 mod 8 columns wide (e.g. head_dim 12, 36, 100, 136, 200, 300) are refused (support is planned for 2.0.1). | `UnsupportedConfigurationError` (a `ValueError`) in the forward, before the op does any work, naming the reason; calls without a backward and `snapshot_quant` `none` are not limited |
|
|
146
|
+
| bf16 snapshots on a float32 bank (`snapshot_quant="bf16"`) on a call that may need a backward | the snapshot is saved in bfloat16, half the size of `none`'s float32 snapshot, on the same CUDA kernels as `none` for every head dim (by code reading). E.g. one SDM layer of `sdm_7B.yaml` (4 heads of 1024 columns, 64 writes per token) with a float32 bank at 128K tokens and batch 1 saves 137 GB of snapshots with `none` (float32), 69 GB with `bf16`, 34 GB with `fp8` and 17 GB with `int4` (computed as tokens x writes x head dims x bytes per element, not measured; fp8 / int4 add one float32 scale per row, about 0.1 GB). On a bfloat16 bank, `snapshot_quant="bf16"` changes nothing: bit-identical to `"none"` (measured on H100 and GB300) | `UnsupportedConfigurationError` (a `ValueError`) before the op does any work, for a call these kernels do not take (including snapshots of `2**31` rows or more); calls without a backward are not limited |
|
|
147
|
+
| `snapshot_quant` on A100 / RTX GPUs | every supported GPU class runs the default `snapshot_quant` `none` on a bfloat16 bank, with no warning except on RTX GPUs. A100-class (sm_80: A100, A30, A800): every mode; tested on A100 with this release (training with unquantized, fp8 and int4 snapshots, and bf16 snapshots on a float32 bank). RTX (sm_86: RTX 30, A10, A40, A16, A2; sm_89: RTX 40, L4, L40, L40S, RTX 6000 Ada; sm_120: RTX 50, RTX PRO 6000 Blackwell): every mode, designed to run on the portable CUDA kernels (chunk_size <= 128); not tested on RTX hardware: each RTX GPU gets one `sparse_delta_memory.UntestedHardwareWarning` per process when the CUDA extensions first load, whatever the snapshot mode (by code reading); A100, H100, B200 and GB300 GPUs never emit it. Inference on these GPUs runs the unquantized path whatever `snapshot_quant` is set (measured on A100; for bf16 snapshots, by code reading) | the warning is a `UserWarning`: silence it with `warnings.filterwarnings("ignore", category=sparse_delta_memory.UntestedHardwareWarning)`; the limits of the other rows (quantized tail head dims, int4 slices 4 mod 8, head_dim above `2**20`, `chunk_size` above 128 on RTX GPUs) apply as stated there |
|
|
148
|
+
| head_dim (`dim / num_heads`) | up to `2**20`; tested and tuned for 64..2048 | `UnsupportedConfigurationError` (a `ValueError`) before the op does any work, on every call |
|
|
149
|
+
| compute and bank dtype | bfloat16, float32 | an error in the forward (float16, float64) |
|
|
150
|
+
| `B * num_heads * slots_per_head` | below `2**31` bank rows (int32 slot ids) | `ValueError` |
|
|
151
|
+
| `B * num_heads * T`, T rounded up to a multiple of `chunk_size` | below `2**31` token rows | `ValueError` in the forward |
|
|
152
|
+
| compact snapshot rows of a training call (about `B * num_heads * T * num_writes`) | any, with 64-bit row offsets from `2**31` rows (tested on GB300 at 2,149,580,800 rows with a bfloat16 bank, `snapshot_quant` `none` and a head_dim that is not a multiple of 4; other head dims, float32 banks and fp8 / int4 snapshots by code reading), except bf16 snapshots on a float32 bank: below `2**31` | `UnsupportedConfigurationError` (a `ValueError`) before the op does any work, for bf16 snapshots on a float32 bank at `2**31` rows or more |
|
|
153
|
+
|
|
154
|
+
Also supported: `key_weighted_decay`, training forward and backward, no-grad prefill with
|
|
155
|
+
bounded working memory, decode, and continuing from a cache.
|
|
156
|
+
|
|
157
|
+
**Input contract.** Each token's W write slots must be distinct, and every slot index must lie
|
|
158
|
+
inside the bank. Out-of-range indices are undefined behaviour and are not checked per call
|
|
159
|
+
(checking would need a device sync). Repeated write slots are handled deterministically (the slot
|
|
160
|
+
decays once), counted on the device, and not refused. The layer's top-k produces distinct,
|
|
161
|
+
in-range slots; the count is read with `memory_ops.repeated_write_slots()`.
|
|
162
|
+
|
|
163
|
+
## Determinism
|
|
164
|
+
|
|
165
|
+
Every output, the final bank and every gradient are **bitwise identical** across repeated calls in
|
|
166
|
+
one process, across fresh processes and with CUDA graphs on or off, for fixed inputs, settings and
|
|
167
|
+
GPU type. It covers float32 and bfloat16 banks, every `snapshot_quant` mode, every chunk size,
|
|
168
|
+
ragged sequences, collision-heavy selections, the training forward, the no-grad prefill and decode.
|
|
169
|
+
|
|
170
|
+
Version 1 added colliding slots with float atomics, in arrival order, so repeated runs on
|
|
171
|
+
identical inputs differed in the last bits, and the next layer's exact top-k turned that into
|
|
172
|
+
different selections.
|
|
173
|
+
|
|
174
|
+
How. Every bank and bank-gradient row has ONE writer per launch (per column slice, where a long row
|
|
175
|
+
is split), and every sum runs in a fixed order: Phase 1 sorts each chunk's entries by slot (stably),
|
|
176
|
+
so a slot's entries form one group; `A`, `QK`, the write mover and the backward's movers sum each
|
|
177
|
+
group in a fixed order and store each row (or cell) once. Everything else was already fixed-order
|
|
178
|
+
(segmented scans, the gather's group partials, the WY solve, the Phase-1 backward, the dual matmul,
|
|
179
|
+
the decode step, product-key top-k), and no kernel is autotuned.
|
|
180
|
+
|
|
181
|
+
Also bitwise, and tested, for the chunked op (more than one token; a single-token no-grad call takes
|
|
182
|
+
the fused decode step, which rounds the updated bank before reading it): the CUDA-graph option's
|
|
183
|
+
eager fallback (a suspended capture, an outer capture, or a call the graphs do not serve) against
|
|
184
|
+
both graph replays and plain eager; and the training forward against the no-grad path of the same
|
|
185
|
+
tokens. Both hold for every snapshot mode, fp8 included, on the calls the write mover of
|
|
186
|
+
`cuda/mover_da2_cuda.py` (or `cuda/mover_fx_cuda.py`, which computes the same bits) serves: compute capability 9.0 or newer, a bfloat16 or float32 bank whose
|
|
187
|
+
head dim splits into equal slices of at most 128 columns (each a multiple of 4 columns and of 16
|
|
188
|
+
bytes), and a geometry that fits shared memory. Other calls run the other deterministic write
|
|
189
|
+
mover, whose fp8 variant sums a slot group's window in another order than the unquantized one at
|
|
190
|
+
head dims below 512 (except 120 on bfloat16 banks): there, with fp8 snapshots, the training forward
|
|
191
|
+
and the no-grad path (and the eager fallback against eager and graph) can differ in the last bits
|
|
192
|
+
of repeated-slot bank rows and of what is read from them. Each path is bitwise reproducible on its
|
|
193
|
+
own.
|
|
194
|
+
|
|
195
|
+
What is NOT promised: different GPU types, library builds or FSDP rank counts (cuBLAS and NCCL
|
|
196
|
+
choose other algorithms and reduction trees); a different `chunk_size`, which is a different
|
|
197
|
+
rounding by design; and the bits of version 1, which agree only to rounding (fixed-order group sums
|
|
198
|
+
versus atomic arrival order) where both select the same slots (Product-key top-k: ties).
|
|
199
|
+
|
|
200
|
+
Memory. The kernels keep Phase 1's group maps, sorted rows and per-entry records for the backward.
|
|
201
|
+
Peak allocated memory: a flagship layer step (2 heads of 1024, `W = R = C = 64`) 12.32 GiB at
|
|
202
|
+
B1 x 32K, 12.82 GiB at B2 x 16K, 13.82 GiB at B4 x 8K; the 24-layer 3:1 model 124.8 GiB at B1 x 32K,
|
|
203
|
+
126.3 GiB at B2 x 16K, 129.4 GiB at B4 x 8K (`benchmarks/release_compare_gb300.json`).
|
|
204
|
+
|
|
205
|
+
Limits. The newer deterministic kernels are chosen by shape, dtype and GPU architecture only,
|
|
206
|
+
never by the data. The Phase-1 pair kernel and the two movers need compute capability 9.0 or
|
|
207
|
+
newer and a geometry that fits shared memory; the forward steps and write-side backward need 8.0
|
|
208
|
+
or newer, `snapshot_quant="none"`, a bfloat16 or float32 bank and a head dim that is a multiple
|
|
209
|
+
of 4. Other calls run the other deterministic kernels: also bitwise reproducible, but slower.
|
|
210
|
+
Parallel streams must use disjoint slots for the whole call. The layer
|
|
211
|
+
gives each stream its own slot range; a direct `sdm_write_read` call that runs several streams over
|
|
212
|
+
ONE shared bank (`num_memory_slots` 0 or the bank's size with `num_parallel > 1`) is refused unless
|
|
213
|
+
it passes `shared_bank=True`, which asserts disjointness. The op does not check the slots
|
|
214
|
+
themselves. Two streams that write one slot in the same chunk step lose one of the two updates;
|
|
215
|
+
any other overlap (a read and a write of one slot, or one
|
|
216
|
+
slot in different steps) loses no update but leaves the step-synchronous order, since the no-grad
|
|
217
|
+
path may run one stream's steps before another's.
|
|
218
|
+
|
|
219
|
+
Determinism outside SDM. This repository's `attn_impl="sdpa"` and `flex_attention` paths, the
|
|
220
|
+
compiled FFN and RMSNorm, and NCCL all-reduce / reduce-scatter on one node measured bitwise on the
|
|
221
|
+
tested stack. A sliding-window FlashAttention-2 backward is bitwise only because a short window
|
|
222
|
+
gives each query block at most two atomic `dq` adds; with a wide window or full attention use the
|
|
223
|
+
library's deterministic backward (+15% on the attention call at window 128). A compiled embedding
|
|
224
|
+
backward over repeated token ids is NOT deterministic in float32 (inductor lowers it to atomic
|
|
225
|
+
scatter-add); the eager one is. Different rank counts are not guaranteed to reproduce bitwise.
|
|
226
|
+
|
|
227
|
+
## Precision
|
|
228
|
+
|
|
229
|
+
- Phase 1 accumulates in float32; `A`, `QK`, `X` and `QX` are float32.
|
|
230
|
+
- The WY solve always uses IEEE float32 dots (Triton `input_precision="ieee"`), whatever
|
|
231
|
+
`torch.backends.cuda.matmul.allow_tf32` says: the inverse amplifies the rounding of its
|
|
232
|
+
inputs. Its launch configuration is pinned, because the warp count changes the rounding.
|
|
233
|
+
Its bits are pinned only for the tested stack (below).
|
|
234
|
+
- Every other float32 matmul follows `torch.backends.cuda.matmul.allow_tf32`: the dual
|
|
235
|
+
matmul of float32 banks and the backward's cuBLAS matmuls. The dual matmul of bfloat16
|
|
236
|
+
banks uses TF32.
|
|
237
|
+
- Autocast is disabled inside the op, so its matmuls keep their float32 operands under an
|
|
238
|
+
enclosing `torch.autocast`, in eager mode and in CUDA graphs alike.
|
|
239
|
+
- `k_val`, `v` and `q_val` are rounded to the bank dtype, `beta` and `g` are used in float32.
|
|
240
|
+
Gathers and movers accumulate in float32. A bank or bank-gradient row is written once per
|
|
241
|
+
chunk: one entry's update is stored once, several entries are summed in float32 in sorted order
|
|
242
|
+
and stored once. The readings come out in the bank dtype (the layer casts them to the compute
|
|
243
|
+
dtype), so a float32 cache on a bfloat16 layer works.
|
|
244
|
+
- With fp8 or int4 snapshots, or bf16 snapshots on a float32 bank, some gradients are approximate
|
|
245
|
+
(see the configuration section).
|
|
246
|
+
- A bfloat16 bank rounds a slot once per chunk that writes it. With realistic
|
|
247
|
+
banks this is a few writes per slot; where slots take tens of writes per chunk (e.g. 256
|
|
248
|
+
writes per token over a 512-slot bank) the final bank deviates from float64 by up to about
|
|
249
|
+
12% of its largest value. Use a float32 bank for such regimes.
|
|
250
|
+
|
|
251
|
+
## Product-key top-k
|
|
252
|
+
|
|
253
|
+
Slot `(a, b)` scores `fp32(A[a]) + fp32(B[b])`, the sum rounded once to float32 (bfloat16
|
|
254
|
+
scores and their float32 upcast select the same slots). Scores compare as float32 numbers
|
|
255
|
+
with `-0` below `+0` and NaN counted as `-inf`. Each factor is ranked by value, larger
|
|
256
|
+
column first on ties; let `i` and `j` be the ranks of `A[a]` and `B[b]`. The candidates are
|
|
257
|
+
the pairs with `(i + 1) * (j + 1) <= k` (any other pair has at least `k` pairs of lower or
|
|
258
|
+
equal ranks that score at least as much), and exact ties among them go to the larger
|
|
259
|
+
`i + j`, then the larger `i`. The returned indices are ascending; the backward is
|
|
260
|
+
deterministic. `product_key_topk` combines any number of factors, in order, with the same
|
|
261
|
+
rule at each stage (see its docstring), and the layer uses the factors of
|
|
262
|
+
`product_key_factor_dims` (two by default). One factor selects its `k` best columns, larger
|
|
263
|
+
column first on ties.
|
|
264
|
+
|
|
265
|
+
Two to four factors of at most 65535 columns and 32 bits of slot index (every configuration of
|
|
266
|
+
version 1) run on `cuda/product_key_topk_cuda.py`, every other factor list (up to 512
|
|
267
|
+
factors) on `cuda/product_key_topk_anyf_cuda.py`; both give the same bits on the factor lists they
|
|
268
|
+
share (tested). Selection time grows with the number of factors and with `k`, ranking time with a
|
|
269
|
+
factor's width.
|
|
270
|
+
|
|
271
|
+
This tie order differs from the previous release of this repository, which selects the
|
|
272
|
+
same score values but may resolve exact ties to other slots. With bfloat16 projections exact
|
|
273
|
+
ties are common: at least one selected slot differed in 35% to 37% of rows on trained
|
|
274
|
+
projections at `k = 64`, and in 73% of rows on randomly initialized ones at `k = 128`. With
|
|
275
|
+
float32 projections ties are rare (1 row in 8192 at `k = 128`).
|
|
276
|
+
|
|
277
|
+
The dense limit: with one factor of width `d_k` and every slot selected
|
|
278
|
+
(`product_key_factor_dims = [d_k]`, `slots_per_head = num_writes = num_reads = d_k`, so
|
|
279
|
+
`d_k <= 256`), `l2_normalize` as `write_act` and `read_act`, and `key_weighted_decay` off,
|
|
280
|
+
each token writes and reads every row of its head's bank, which becomes the `d_k x d_v` state
|
|
281
|
+
of the gated delta rule (the recurrence of Gated DeltaNet). Without output gate or readings
|
|
282
|
+
LayerNorm and from a zero initial bank, the layer then computes that recurrence.
|
|
283
|
+
`tests/test_dense_limit_gdn.py` checks the op (training, no-grad, CUDA-graph and decode
|
|
284
|
+
paths, also from a nonzero initial bank) and the layer against a float64 reference of it,
|
|
285
|
+
with `snapshot_quant` `none`: they agree up to floating-point rounding (fp8 and int4 snapshots,
|
|
286
|
+
and bf16 snapshots on a float32 bank, make the gradients approximate).
|
|
287
|
+
|
|
288
|
+
## CUDA graphs
|
|
289
|
+
|
|
290
|
+
After the first calls, the training forward and backward do not synchronize with the host
|
|
291
|
+
(check with `torch.cuda.set_sync_debug_mode("error")`), so a whole training step can be
|
|
292
|
+
captured with `torch.cuda.graph`. Run the step eagerly at the exact shapes first: the first
|
|
293
|
+
calls build the CUDA extensions, compile the Triton kernels and create a small counter per
|
|
294
|
+
device.
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
# layer: a SparseDeltaMemory converted to bf16, in train mode.
|
|
298
|
+
static_x = torch.randn(B, T, args.dim, device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
|
299
|
+
s = torch.cuda.Stream()
|
|
300
|
+
s.wait_stream(torch.cuda.current_stream())
|
|
301
|
+
with torch.cuda.stream(s): # eager warmup at the captured shapes
|
|
302
|
+
for _ in range(3):
|
|
303
|
+
out, _ = layer(static_x)
|
|
304
|
+
out.float().square().mean().backward()
|
|
305
|
+
torch.cuda.current_stream().wait_stream(s)
|
|
306
|
+
layer.zero_grad(set_to_none=True) # the graph owns the gradients
|
|
307
|
+
static_x.grad = None
|
|
308
|
+
graph = torch.cuda.CUDAGraph()
|
|
309
|
+
with torch.cuda.graph(graph):
|
|
310
|
+
static_out, _ = layer(static_x)
|
|
311
|
+
static_out.float().square().mean().backward()
|
|
312
|
+
# Each step: copy new data into the static input and replay.
|
|
313
|
+
with torch.no_grad():
|
|
314
|
+
static_x.copy_(new_x)
|
|
315
|
+
graph.replay() # static_out and every .grad now hold this step
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
A graph is valid for one set of shapes, dtypes and layer settings. The statistics options
|
|
319
|
+
(`log_memory_access_stats`, `log_memory_norms`) read values on the host; they are collected
|
|
320
|
+
in eval mode only and skipped during a capture. `forward` is wrapped in
|
|
321
|
+
`@torch.compiler.disable`: `torch.compile` compiles the rest of the model and runs this layer
|
|
322
|
+
eagerly. The decode step can be captured the same way; `apps/main/generate.py` does so for
|
|
323
|
+
SDM models (`sdm_decode_cuda_graph`, default true): the first decode step runs eager, the next
|
|
324
|
+
is captured, and every later step of every prompt replays it (greedy decoding is
|
|
325
|
+
token-identical to eager, the logits bitwise). It is captured again when a tensor it reads
|
|
326
|
+
moved or changed shape (checked once per prompt) or a matmul or attention-backend setting
|
|
327
|
+
changed; steps under autocast, and all steps after a failed capture, run eager.
|
|
328
|
+
|
|
329
|
+
### The `cuda_graph` option
|
|
330
|
+
|
|
331
|
+
`cuda_graph: true` makes each layer replay its write/read (the bank expand, the op forward
|
|
332
|
+
and backward, and the bank-gradient reduction) from two CUDA graphs, captured at the first
|
|
333
|
+
call that may need a backward (the forward) and at its backward (the backward), once per
|
|
334
|
+
layer or, under activation checkpointing, once per set of layers sharing them (see Memory
|
|
335
|
+
below). Projections, top-k and gates stay eager, so
|
|
336
|
+
FSDP, activation checkpointing (including lingua's selective policy) and `torch.compile` of
|
|
337
|
+
the surrounding model work unchanged. The field defaults to false. `debug_sdm.yaml`,
|
|
338
|
+
`debug_hybrid.yaml` and `sdm_flagship.yaml` set it; `sdm_7B.yaml` leaves it off (chunk 256 leaves
|
|
339
|
+
few launches to save). In a config of your
|
|
340
|
+
own, add `cuda_graph: true` under `model.sdm_args` (or pass `model.sdm_args.cuda_graph=true`).
|
|
341
|
+
|
|
342
|
+
The graphs serve one fixed call per layer, and every other call runs eager, so the option
|
|
343
|
+
changes no result beyond floating-point rounding:
|
|
344
|
+
- Calls without a backward (evaluation, prefill, decode) run eager.
|
|
345
|
+
- A differentiable call that the graphs do not serve runs eager as well, and saves for its
|
|
346
|
+
backward what a replayed call saves (its bank parameter and inputs): its backward runs its
|
|
347
|
+
forward again. So under non-reentrant checkpointing a forward and its recompute may take
|
|
348
|
+
different paths. These are:
|
|
349
|
+
- a call of another shape than the captured one (or under another
|
|
350
|
+
`torch.backends.cuda.matmul.allow_tf32` setting), with a warning once per layer. After a
|
|
351
|
+
permanent change, call `cuda_graph.reset_cuda_graphs()` between steps to capture the new
|
|
352
|
+
shape; graphs are never re-captured on their own;
|
|
353
|
+
- every call inside `with suspend_cuda_graphs():`, which `apps/main/train.py` uses for its
|
|
354
|
+
probe (a smaller batch);
|
|
355
|
+
- every call inside a CUDA-graph capture of your own (with a whole-step capture, leave the
|
|
356
|
+
option off: the captured backward would run each forward again);
|
|
357
|
+
- every call of a layer whose capture failed or whose first backward ran out of memory,
|
|
358
|
+
until `reset_cuda_graphs()` (one warning).
|
|
359
|
+
- A differentiable call that starts from a `cache` runs eager (one warning).
|
|
360
|
+
- A backward whose graph state was overwritten before it ran (two forwards of a layer before
|
|
361
|
+
one backward, a layer called twice per forward, a forward of another graphed model or of a
|
|
362
|
+
layer sharing the graphs in between, a second backward with `retain_graph=True`) first
|
|
363
|
+
replays its forward from the saved inputs: same gradients, one more forward per such
|
|
364
|
+
backward. A checkpoint recompute right before its backward needs no extra replay (a layer
|
|
365
|
+
called twice in one checkpoint region still replays the forward of its first call).
|
|
366
|
+
- The state a replayed call returns (`new_cache`) never exposes the graphs' static bank: its
|
|
367
|
+
`memory` is a private copy of the call's final bank, taken at the first read, which is valid
|
|
368
|
+
until the next SDM forward or backward that reuses the call's graph memory (a later forward
|
|
369
|
+
of the layer, of a layer sharing its graphs or of a layer captured before it, or the call's
|
|
370
|
+
backward). A first read after that raises a `RuntimeError` naming the fix: read the state
|
|
371
|
+
right after the call and keep that copy (or a clone), or run the call under
|
|
372
|
+
`suspend_cuda_graphs()`. After the backward, the state of a plain eager call (option off)
|
|
373
|
+
holds the restored initial bank; a copy read earlier from a replayed call, like the bank of
|
|
374
|
+
the option's eager fallback, keeps the final bank.
|
|
375
|
+
- Memory: without activation checkpointing each layer keeps static input, output and gradient
|
|
376
|
+
buffers and its graph state for the whole step (eager keeps that state too). Under
|
|
377
|
+
non-reentrant activation checkpointing (`torch.utils.checkpoint`, also with a selective
|
|
378
|
+
policy), layers of one shape whose calls run in different checkpoint regions share one set
|
|
379
|
+
of graphs, buffers and state, since each backward follows its own recompute: with one
|
|
380
|
+
region per block, the option adds about one layer's state and buffers in all. Layers of one
|
|
381
|
+
shape in the same region keep one set each, because the region's recompute runs them all
|
|
382
|
+
before their backwards (eager then holds all their states too); lingua's selective policy
|
|
383
|
+
wraps the whole model in one region. A layer's sharing is set by its first graphed call
|
|
384
|
+
(until `reset_cuda_graphs()`); a layer first called in a region nested in another may share
|
|
385
|
+
the state of an earlier layer of the outer region, which then replays its forward once more.
|
|
386
|
+
Calls from several threads on graphs that share memory run one at a time.
|
|
387
|
+
- Layers first called on different CUDA streams capture into different memory pools; a
|
|
388
|
+
replay on another stream than its capture's is ordered after the work queued on that one.
|
|
389
|
+
|
|
390
|
+
The option removes host launch overhead: 1.04x to 1.09x for the flagship layer at 32K tokens per
|
|
391
|
+
step on GB300 (`benchmarks/RESULTS.md`). In the 24-layer 3:1 model it adds 7.7 GiB to the peak
|
|
392
|
+
allocated memory (124.8 to 132.6 GiB at B1 x 32K). A whole-step capture of your own (above)
|
|
393
|
+
removes the overhead of the whole model and needs no option.
|
|
394
|
+
|
|
395
|
+
## Activation checkpointing and the bank state
|
|
396
|
+
|
|
397
|
+
The op keeps everything its backward reads in `save_for_backward`, and it hides its internal
|
|
398
|
+
ops from torch dispatch modes, so selective activation checkpointing neither caches nor
|
|
399
|
+
intercepts them: under non-reentrant checkpointing the op is recomputed as a whole, which
|
|
400
|
+
costs one extra forward of every SDM layer, and the gradient is that of the recomputed
|
|
401
|
+
forward. Reentrant checkpointing runs the first forward without grad, which saves nothing.
|
|
402
|
+
The same opacity applies to every dispatch mode: `FlopCounterMode` does not count the op,
|
|
403
|
+
and fake or meta tensors are not supported.
|
|
404
|
+
|
|
405
|
+
The forward advances the bank in place and the backward restores it in place: exactly with
|
|
406
|
+
`snapshot_quant` `none`; with fp8 or int4, the written rows come back as their dequantized
|
|
407
|
+
snapshot, not their original bits (a data-dependent error, about 2.5% and 10% relative RMS
|
|
408
|
+
on those rows for random test inputs); with bf16 snapshots on a float32 bank, as their bfloat16
|
|
409
|
+
rounding.
|
|
410
|
+
For a call that may need a backward, the
|
|
411
|
+
returned `SDMLayerState` (and a passed-in cache) therefore holds the final bank only until
|
|
412
|
+
backward runs; clone `state.memory` before backward to carry it to the next segment.
|
|
413
|
+
Inference calls are unaffected. For the same reason, a second backward through one op call
|
|
414
|
+
(`retain_graph=True`, then another backward) would return a wrong gradient, so it raises a
|
|
415
|
+
RuntimeError.
|
|
416
|
+
The `cuda_graph` option's calls are the exception: their second backward first replays the
|
|
417
|
+
forward and is correct (not a differentiable call that starts from a cache, which runs the
|
|
418
|
+
plain op).
|
|
419
|
+
|
|
420
|
+
## Configuration: `SparseDeltaMemoryArgs` (`model.sdm_args` in the configs)
|
|
421
|
+
|
|
422
|
+
| Field | Meaning |
|
|
423
|
+
|---|---|
|
|
424
|
+
| `dim`, `num_heads` | model width and number of heads; head_dim = `dim / num_heads` |
|
|
425
|
+
| `slots_per_head` | bank rows per head, `side ** 2` for two product-key factors of `side` entries |
|
|
426
|
+
| `product_key_factor_dims` | per-head product-key factor widths, any number of them (`None`: two factors of `sqrt(slots_per_head)`); their product must equal `slots_per_head` |
|
|
427
|
+
| `num_writes`, `num_reads` | slots written / read per token and head (top-k width; default 64) |
|
|
428
|
+
| `chunk_size` | chunk size `C` of the op (`None`: 64; speed; results change only by floating-point rounding; tuned configs set 128 or 256 explicitly). `memory_block_size`, its former name, is still accepted |
|
|
429
|
+
| `read_act`, `write_act`, `*_act_scale` | slot-weight activation: `Softmax` (scaled), `l2_normalize`, or a `torch.nn` activation name |
|
|
430
|
+
| `normalize_readings`, `output_gate` | per-head LayerNorm on readings; sigmoid output gate |
|
|
431
|
+
| `A_init_range` | decay initialization range; `dt_bias` follows GDN / FLA (`dt_bias_init` is not used) |
|
|
432
|
+
| `backprop_on_memory`, `query_batchnorm` | learn the initial bank; BatchNorm on the key and query projections |
|
|
433
|
+
| `key_weighted_decay` | per-write decay `exp(g * k_val)` |
|
|
434
|
+
| `snapshot_quant` | storage of the backward snapshot: `none`, `bf16`, `fp8`, `int4` |
|
|
435
|
+
| `log_memory_access_stats`, `log_memory_norms` | opt-in eval-mode statistics |
|
|
436
|
+
| `cuda_graph` | replay the write/read of differentiable calls from per-layer CUDA graphs (on in the debug and flagship configs) |
|
|
437
|
+
| `grad_bank_fp32` | opt-in (default `false`): the backward accumulates the bank gradient in float32 and casts it back once; no effect on a float32 bank |
|
|
438
|
+
|
|
439
|
+
`snapshot_quant`: the training forward saves, for every token and write slot, the bank row
|
|
440
|
+
read before decay (`B * T * num_writes * head_dim` elements per head group). `fp8` stores it
|
|
441
|
+
as `float8_e4m3fn` and `int4` as packed 4-bit values, each with one scale per row (a
|
|
442
|
+
bfloat16-rounded value stored as float32), instead of the bank dtype. The forward computes the same
|
|
443
|
+
values (its write mover is specialized per snapshot mode, so with `fp8` below head_dim 512 the bank
|
|
444
|
+
update can round differently in the last bits; see Determinism); the gradients of
|
|
445
|
+
`k_val`, the gates and `q_val` read the dequantized rows and are approximate, and the
|
|
446
|
+
backward restores the written bank rows from them. `bf16` on a float32 bank stores the rows
|
|
447
|
+
rounded to bfloat16 (round to nearest even), half the size of the float32 snapshot, on the same
|
|
448
|
+
CUDA kernels as `none` (the forward is `none`'s, bit for bit, measured on H100 and GB300; the same
|
|
449
|
+
three gradients read the bfloat16 rows). On a bfloat16 bank, `snapshot_quant="bf16"` changes
|
|
450
|
+
nothing: bit-identical to `"none"` (measured on H100 and GB300), whose snapshot is bfloat16 already.
|
|
451
|
+
|
|
452
|
+
## Changes from the previous release
|
|
453
|
+
|
|
454
|
+
See `CHANGELOG.md` in the repository. For every configuration of the previous release that this
|
|
455
|
+
version accepts (two square product-key factors: `product_key_factor_dims` left at `None`),
|
|
456
|
+
parameter names and shapes are unchanged and previous state dicts load with `strict=True`; other
|
|
457
|
+
factor lists can change the width of the key and query projections (and of their BatchNorm with
|
|
458
|
+
`query_batchnorm`).
|
|
459
|
+
|
|
460
|
+
## Build requirements
|
|
461
|
+
|
|
462
|
+
- A CUDA GPU, a CUDA build of PyTorch, a CUDA toolkit matching `torch.version.cuda`
|
|
463
|
+
(`CUDA_HOME` must contain `bin/nvcc`, or set `PYTORCH_NVCC`) with its CUB headers, a host
|
|
464
|
+
C++ compiler and `ninja`.
|
|
465
|
+
- The CUDA extensions (Phase 1 and its backward, the gathers, the movers and the backward's
|
|
466
|
+
kernels, the product-key top-k) are built with `torch.utils.cpp_extension` on first use, never
|
|
467
|
+
at import, and cached in `TORCH_EXTENSIONS_DIR`. With a cold cache the first call compiles them:
|
|
468
|
+
about 6 minutes for a no-grad or eval call and 10 minutes for a training call on a GB300 node
|
|
469
|
+
(each process that finds them cached loads them in well under a second).
|
|
470
|
+
- Builds are named `<base>_<16 hex digits>`, hashing sources, bindings, flags,
|
|
471
|
+
torch/CUDA/Python versions, `CUDA_HOME` and the target GPU architectures, so a stale build
|
|
472
|
+
is never reused and different source trees can share one cache directory.
|
|
473
|
+
- A build killed mid-compile leaves a `lock` file in its build directory that later loads
|
|
474
|
+
wait on; the loader warns, and the file can be deleted if no build is running.
|
|
475
|
+
- Tested with torch 2.13.0 (CUDA 13.0), triton 3.7.1 and Python 3.12 on NVIDIA GB300
|
|
476
|
+
(sm_103); the test suite also passes on H100 (sm_90). Other GPUs are untested.
|
|
477
|
+
|
|
478
|
+
## Benchmarks
|
|
479
|
+
|
|
480
|
+
Speed against the previous release: the repository README (Speed) and `benchmarks/RESULTS.md` (all
|
|
481
|
+
tables, setup and spreads); records: `benchmarks/release_compare_gb300.json`; scripts:
|
|
482
|
+
`benchmarks/README.md`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
sparse_delta_memory/__init__.py,sha256=yTKlkNZOqiu3lY3Ge0mpKQpOGr4cG-kFVJpRMi5CRNo,619
|
|
2
|
+
sparse_delta_memory/backward.py,sha256=_zqaFBLTtehLJjj8aflWzqBUSw0xjxINbEY5bPbPIjE,23842
|
|
3
|
+
sparse_delta_memory/cache.py,sha256=8_h2Qy12YZGtf1IgE2rFBGUESLMrLI2DIIxfWQipsYM,1827
|
|
4
|
+
sparse_delta_memory/cuda_graph.py,sha256=YX_KrRbUA-8tkPnEsz5rEI-rff-jJEDA5QxqzfjbLvQ,29181
|
|
5
|
+
sparse_delta_memory/decode.py,sha256=-6WN4sEM7eUqIf6g2Ba28q2-RduQOkzZlwGziEjMQH8,6861
|
|
6
|
+
sparse_delta_memory/forward.py,sha256=S1SbuMofehqFRpl9Rz122CbMUIXrY18-CZLEiqrqgQw,30756
|
|
7
|
+
sparse_delta_memory/layer.py,sha256=Iw_r21uIpIMyiX-Y2bwAhiZvvtj2t0n9iPIdInyL0Kw,31188
|
|
8
|
+
sparse_delta_memory/memory_ops.py,sha256=MY_O65M0Rrg3FlFoVBVOfFe5YVDy1Nauwhpm7k1eRYc,53710
|
|
9
|
+
sparse_delta_memory/memory_stats.py,sha256=PWfOlh_yNhUammdh2gJYiIGHxKMcvvbug3skKysgrtg,10891
|
|
10
|
+
sparse_delta_memory/movers.py,sha256=U0Jjdx21A0B6gnMMoNuJuM1WuBMfugoawVmhmPHR7YM,11849
|
|
11
|
+
sparse_delta_memory/phase2_kernels.py,sha256=kdTqF5W8bPX8N7C3fCuuZQvcZSrvbZNpWsGvklpMukQ,12858
|
|
12
|
+
sparse_delta_memory/product_key_topk.py,sha256=fWIyby9tZqml1rW4nHB06igsZuu-TC6D2kCLVngrAlU,5912
|
|
13
|
+
sparse_delta_memory/wy_solve.py,sha256=Hfgcuh4it6oA4H-Dikspqdr_B3wPMCPr_Oy_zxzz8mk,11642
|
|
14
|
+
sparse_delta_memory/cuda/__init__.py,sha256=3k2TiiK0Cv5-jAfq36MyfOOLVbbxYw7c4sLTS0i_H9Q,229
|
|
15
|
+
sparse_delta_memory/cuda/_build.py,sha256=TZhLN2zydgSzQIVcB5s40dM8enKPR3spbZhjo1oFczA,10593
|
|
16
|
+
sparse_delta_memory/cuda/_device.py,sha256=jN9iuF-OLHHIk4ryeFJcETbHd8nZCyLvkAZNjwKTC3g,7032
|
|
17
|
+
sparse_delta_memory/cuda/_devmemo.py,sha256=lDB1T4GkcrGdeyZlCsz0GHEaxGIBTMYl7Xwlbld8Us8,2193
|
|
18
|
+
sparse_delta_memory/cuda/_image.py,sha256=Y8Hr72OQFIK-fvOWHEkOXac2Aw-88j5TJdXt_HivzJk,6497
|
|
19
|
+
sparse_delta_memory/cuda/bwd_steps_cuda.py,sha256=zbUlL5tsh-KK-x042o46_YULmNdV-hqYfB0xMMFfMjg,5509
|
|
20
|
+
sparse_delta_memory/cuda/gather_cuda.py,sha256=q0sg3xkvrr5zPcw_31QZuoejoYXenEseYC774XarywM,36202
|
|
21
|
+
sparse_delta_memory/cuda/gather_ns_cuda.py,sha256=6lAaQKMraKqdr8FwVjecGGSnA1HxFIiYKFUO5BaR8n0,7676
|
|
22
|
+
sparse_delta_memory/cuda/gx_gds_cuda.py,sha256=M7r3FL8Ix2xS36RcK1tFlxbXH8nyhCWb36TNMxHNEg0,73393
|
|
23
|
+
sparse_delta_memory/cuda/gx_gemm_cuda.py,sha256=qeivM4bc47l7C8sG3y5I4uwyxcJbBDgqQqOf7-t-oOI,44451
|
|
24
|
+
sparse_delta_memory/cuda/gx_pa2_cuda.py,sha256=h1hVwdPqvpT0R4UjtcwqOsjNB-P7lwwqEpUzi6JvPSE,83547
|
|
25
|
+
sparse_delta_memory/cuda/mover_da2_cuda.py,sha256=vJl_6bTVAbjUZ7KorTxx1whBP_cW8P8-UMAoBXy1tpA,98722
|
|
26
|
+
sparse_delta_memory/cuda/mover_fx_cuda.py,sha256=zP7QbD0TQsEA4tOV2SKrvR4jTtZ_2Brz6llSqA7OIvM,134471
|
|
27
|
+
sparse_delta_memory/cuda/pa_dc_cuda.py,sha256=EPPGEOvIWnONfTTDXLL3at7X8YODMG4ejKQGHGJkqnk,84403
|
|
28
|
+
sparse_delta_memory/cuda/phase1_bwd_cuda.py,sha256=b8t6SWURU_9rMgHIEOfuZkWgeVzOXanw2D8ElZZH83Q,38290
|
|
29
|
+
sparse_delta_memory/cuda/phase1_common.cuh,sha256=S0paaGQNAjnpJ0zMM6aNFMknpkTJ0c7KkS93pezJ3oA,8053
|
|
30
|
+
sparse_delta_memory/cuda/phase1_cuda.py,sha256=RaQW0GcUggjIgk9-xwHVsr2EoQvg8x_oxtoncch_uWo,201666
|
|
31
|
+
sparse_delta_memory/cuda/product_key_topk_anyf_cuda.py,sha256=xDN7NEXL0wZ8eyKcOIKZXBSklVim-uMo7DAg-fGeXdw,27972
|
|
32
|
+
sparse_delta_memory/cuda/product_key_topk_cuda.py,sha256=5QSOjezkRn6GLjmxQxLIqSFTx2zG1FJ3bJA1_Tf8WeU,30294
|
|
33
|
+
sparse_delta_memory/cuda/px_qrec_cuda.py,sha256=cdZEwHBK_axURCq17j5A9n06dPFMepxYjsbehwPmiUc,8377
|
|
34
|
+
sparse_delta_memory/cuda/sdm_copy.cuh,sha256=SaXehTNmb_FW0cSK7JQXpacb1z3N_xxsUSlCEr3OM-Y,5493
|
|
35
|
+
sparse_delta_memory/cuda/sdm_plancap.cuh,sha256=PTw7Mvaq6cNBKxGLA62v17qXZDevyrgrDggPzg9eFos,6725
|
|
36
|
+
sparse_delta_memory-2.0.0.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
37
|
+
sparse_delta_memory-2.0.0.dist-info/METADATA,sha256=DajcJFrO0a5tX9Jt6ijutQ9fCqe1aXClgjMooxbFCRA,35305
|
|
38
|
+
sparse_delta_memory-2.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
39
|
+
sparse_delta_memory-2.0.0.dist-info/top_level.txt,sha256=SRKYcpRllz0DS5kavIRvD6w72dvEauj4NsDu1SD4FEc,20
|
|
40
|
+
sparse_delta_memory-2.0.0.dist-info/RECORD,,
|