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,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
|