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,181 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ //
3
+ // Device building blocks shared by the Phase-1 forward and backward kernels.
4
+ //
5
+ // Sorted layout
6
+ // -------------
7
+ // A chunk holds C tokens. Token t writes W slots (K entries) and reads R slots (Q entries).
8
+ // Its n = C * (W + R) entries are enumerated e = t * (W + R) + j: K entry (t, j) for
9
+ // j < W, then Q entry (t, j - W). Each chunk is one row of the sorted arrays [N, row],
10
+ // row = n rounded up to a multiple of 16 (entries [n, row) are unused). The row holds the
11
+ // chunk's entries stably sorted by key = slot id relative to the chunk's stream, so each
12
+ // group (the maximal run of one slot) is ordered by (token, K before Q, lane).
13
+ // Entries of tokens at or past the chunk's valid length are invalid: they take the
14
+ // largest key, sort after every valid entry and are each a group of their own.
15
+ //
16
+ // Payload of a sorted entry:
17
+ // bits 0-7 lane (K: 0..W-1, Q: 0..R-1)
18
+ // bit 8 Q entry
19
+ // bits 9-16 token
20
+ // bit 17 invalid
21
+ // bit 30 last K entry of its group (set by the forward scan)
22
+ // bit 31 first entry of its group (set by the forward scan)
23
+ //
24
+ // x is the log-decay an entry contributes: the gate of its token (of its lane with
25
+ // per-write gates) on the first K entry of each (slot, token); zero on Q entries, invalid
26
+ // entries and repeated K entries of one token (repeats break the distinct-slot contract
27
+ // and are counted). An entry's log-cumsum is the inclusive sum of x over its group
28
+ // prefix, so a Q entry sees the K entries of its own token. Log-cumsums are compensated
29
+ // sums: the decay between two entries of a group is the exp of their difference, and the
30
+ // compensation keeps its error at the rounding of the difference rather than of the
31
+ // (possibly much larger) cumsums.
32
+ #pragma once
33
+
34
+ #include <cstdint>
35
+ #include <cstring>
36
+
37
+ #include <cuda_bf16.h>
38
+
39
+ namespace sdm_p1 {
40
+
41
+ constexpr uint32_t kLaneMask = 0xFFu;
42
+ constexpr uint32_t kIsQ = 1u << 8;
43
+ constexpr int kTokenShift = 9;
44
+ constexpr uint32_t kInvalid = 1u << 17;
45
+ constexpr uint32_t kLastK = 1u << 30;
46
+ constexpr uint32_t kHead = 1u << 31;
47
+ constexpr int kRowAlign = 16;
48
+ constexpr float kLog2e = 1.4426950408889634f;
49
+
50
+ // A thread's consecutive row entries, moved with 16-byte accesses. Rows are padded to a
51
+ // multiple of kRowAlign entries, so a thread's items never straddle a row end.
52
+ template <typename T, int ITEMS>
53
+ __device__ __forceinline__ void load_items(const T* src, T (&dst)[ITEMS]) {
54
+ constexpr int kVecs = sizeof(dst) / sizeof(uint4);
55
+ uint4 v[kVecs];
56
+ #pragma unroll
57
+ for (int i = 0; i < kVecs; ++i) v[i] = reinterpret_cast<const uint4*>(src)[i];
58
+ memcpy(dst, v, sizeof(dst));
59
+ }
60
+
61
+ template <typename T, int ITEMS>
62
+ __device__ __forceinline__ void store_items(T* dst, const T (&src)[ITEMS]) {
63
+ constexpr int kVecs = sizeof(src) / sizeof(uint4);
64
+ uint4 v[kVecs];
65
+ memcpy(v, src, sizeof(src));
66
+ #pragma unroll
67
+ for (int i = 0; i < kVecs; ++i) reinterpret_cast<uint4*>(dst)[i] = v[i];
68
+ }
69
+
70
+ __device__ __forceinline__ int lane_of(uint32_t p) { return p & kLaneMask; }
71
+ __device__ __forceinline__ int token_of(uint32_t p) { return (p >> kTokenShift) & 0xFF; }
72
+ __device__ __forceinline__ bool is_head(uint32_t p) { return p & kHead; }
73
+ __device__ __forceinline__ bool is_k(uint32_t p) { return !(p & (kIsQ | kInvalid)); }
74
+ __device__ __forceinline__ bool is_q(uint32_t p) { return (p & (kIsQ | kInvalid)) == kIsQ; }
75
+
76
+ // A K entry that repeats the slot of an earlier K entry of the same token. Such entries
77
+ // are adjacent in sorted order, directly after the token's first K entry of that slot.
78
+ __device__ __forceinline__ bool repeats_k(uint32_t prev, uint32_t cur) {
79
+ return !is_head(cur) && is_k(cur) && is_k(prev) && token_of(prev) == token_of(cur);
80
+ }
81
+
82
+ __device__ __forceinline__ float to_float(float v) { return v; }
83
+ __device__ __forceinline__ float to_float(__nv_bfloat16 v) { return __bfloat162float(v); }
84
+ template <typename T> __device__ __forceinline__ T from_float(float v);
85
+ template <> __device__ __forceinline__ float from_float<float>(float v) { return v; }
86
+ template <> __device__ __forceinline__ __nv_bfloat16 from_float<__nv_bfloat16>(float v) {
87
+ return __float2bfloat16(v);
88
+ }
89
+
90
+ // Segmented-scan element: a compensated sum of x (value `x + c`, where c collects the
91
+ // rounding error of every addition) and a K count in bits 0-30; bit 31 starts a segment.
92
+ // Compensation matters because exponentiating a log-sum turns its absolute error into a
93
+ // relative one.
94
+ struct SegSum {
95
+ float x;
96
+ float c;
97
+ uint32_t kf;
98
+ };
99
+
100
+ __device__ __forceinline__ SegSum seg_combine(SegSum a, SegSum b) {
101
+ if (b.kf & kHead) return b;
102
+ const float s = a.x + b.x;
103
+ const float bb = s - a.x;
104
+ const float err = (a.x - (s - bb)) + (b.x - bb);
105
+ return {s, a.c + b.c + err, a.kf + b.kf};
106
+ }
107
+
108
+ __device__ __forceinline__ float seg_value(SegSum s) { return s.x + s.c; }
109
+ __device__ __forceinline__ uint32_t seg_count(SegSum s) { return s.kf & ~kHead; }
110
+
111
+ template <bool REVERSE>
112
+ __device__ __forceinline__ SegSum shfl_prev(SegSum v, int delta) {
113
+ if (REVERSE) {
114
+ return {__shfl_down_sync(0xFFFFFFFFu, v.x, delta),
115
+ __shfl_down_sync(0xFFFFFFFFu, v.c, delta),
116
+ __shfl_down_sync(0xFFFFFFFFu, v.kf, delta)};
117
+ }
118
+ return {__shfl_up_sync(0xFFFFFFFFu, v.x, delta), __shfl_up_sync(0xFFFFFFFFu, v.c, delta),
119
+ __shfl_up_sync(0xFFFFFFFFu, v.kf, delta)};
120
+ }
121
+
122
+ // Shared state of block_scan_exclusive (blocks of up to 1024 threads).
123
+ struct BlockScanSmem {
124
+ SegSum warp_total[32];
125
+ SegSum warp_prefix[32];
126
+ SegSum block_total;
127
+ };
128
+
129
+ // Segmented scan across a block. Scan order runs over threads in increasing index, or in
130
+ // decreasing index when REVERSE, and each thread passes the combined value of its own
131
+ // items in that order. Returns the combination of everything before the thread, starting
132
+ // from `carry` (earlier tiles), and advances `carry` past this tile. Every thread of the
133
+ // block must call it.
134
+ template <int THREADS, bool REVERSE>
135
+ __device__ SegSum block_scan_exclusive(SegSum own, SegSum& carry, BlockScanSmem& smem) {
136
+ constexpr int kWarps = THREADS / 32;
137
+ const int lane = threadIdx.x & 31;
138
+ const int warp = threadIdx.x >> 5;
139
+ const int rank = REVERSE ? 31 - lane : lane;
140
+ SegSum inclusive = own;
141
+ #pragma unroll
142
+ for (int d = 1; d < 32; d <<= 1) {
143
+ const SegSum other = shfl_prev<REVERSE>(inclusive, d);
144
+ if (rank >= d) inclusive = seg_combine(other, inclusive);
145
+ }
146
+ const SegSum before = shfl_prev<REVERSE>(inclusive, 1);
147
+ if (rank == 31) smem.warp_total[warp] = inclusive;
148
+ __syncthreads();
149
+ if (warp == 0) {
150
+ // Lane l scans the totals of the warps in scan order, seeded with the carry.
151
+ const int w = REVERSE ? kWarps - 1 - lane : lane;
152
+ SegSum v = lane < kWarps ? smem.warp_total[w] : SegSum{0.0f, 0.0f, 0u};
153
+ #pragma unroll
154
+ for (int d = 1; d < kWarps; d <<= 1) {
155
+ const SegSum other = shfl_prev<false>(v, d);
156
+ if (lane >= d) v = seg_combine(other, v);
157
+ }
158
+ const SegSum earlier = shfl_prev<false>(v, 1);
159
+ if (lane < kWarps) smem.warp_prefix[w] = lane == 0 ? carry : seg_combine(carry, earlier);
160
+ if (lane == kWarps - 1) smem.block_total = seg_combine(carry, v);
161
+ }
162
+ __syncthreads();
163
+ carry = smem.block_total;
164
+ const SegSum prefix = smem.warp_prefix[warp];
165
+ return rank == 0 ? prefix : seg_combine(prefix, before);
166
+ }
167
+
168
+ // The log-cumsum of `s` in log2 units, as a compensated pair hi + lo whose low part also
169
+ // carries the rounding error of the product.
170
+ __device__ __forceinline__ void log2_parts(SegSum s, float& hi, float& lo) {
171
+ hi = s.x * kLog2e;
172
+ lo = fmaf(s.c, kLog2e, fmaf(s.x, kLog2e, -hi));
173
+ }
174
+
175
+ // log2 of the decay between entries p < r of one group, exp(sum of x over (p, r]), from
176
+ // their log-cumsums in log2 units held as compensated pairs hi + lo.
177
+ __device__ __forceinline__ float log2_decay(float hi_r, float lo_r, float hi_p, float lo_p) {
178
+ return (hi_r - hi_p) + (lo_r - lo_p);
179
+ }
180
+
181
+ } // namespace sdm_p1