tileops 0.0.1.dev0__py3-none-any.whl → 0.0.1.dev1__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.
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: tileops
3
+ Version: 0.0.1.dev1
4
+ Summary: TileOPs kernels for efficient inference
5
+ Author: Tile-AI
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.8
8
+ License-File: LICENSE
9
+ Requires-Dist: torch>=2.1.0
10
+ Requires-Dist: numpy>=1.23.5
11
+ Requires-Dist: einops>=0.6.0
12
+ Requires-Dist: Cython
13
+ Requires-Dist: tqdm>=4.62.3
14
+ Requires-Dist: typing_extensions>=4.10.0
15
+ Requires-Dist: cloudpickle
16
+ Requires-Dist: ml_dtypes
17
+ Requires-Dist: psutil
18
+ Requires-Dist: tilelang>=0.1.6.post1
19
+ Provides-Extra: dev
20
+ Requires-Dist: yapf==0.40.2; extra == "dev"
21
+ Requires-Dist: toml==0.10.2; extra == "dev"
22
+ Requires-Dist: tomli==2.0.1; extra == "dev"
23
+ Requires-Dist: ruff==0.6.5; extra == "dev"
24
+ Requires-Dist: codespell==2.3.0; extra == "dev"
25
+ Requires-Dist: clang-format==15.0.7; extra == "dev"
26
+ Dynamic: license-file
@@ -0,0 +1,19 @@
1
+ tileops-0.0.1.dev1.dist-info/licenses/LICENSE,sha256=hr_8lcbfSW8PTqqDHjB_EfPueXTmbaAz7nDy2F97vl0,1126
2
+ top/__init__.py,sha256=lop9i9TF0vfH-gY0XPTyBvK__TjV2R7jQbhk5PvHqnc,857
3
+ top/kernel/__init__.py,sha256=IVHXz3zMwHXfhm3Rl7PB8c7kFsjEKEyXi7AhfkDzIZ0,735
4
+ top/kernel/bitnet.py,sha256=EIHKwqxi8x85kDuik2d0GeYJyeJfKNVYisF2ix367AI,18011
5
+ top/kernel/blocksparse_attention.py,sha256=EZPtzeFH5LjeGiQzlg8v7ChGEyXozFaS9B841F59DN4,26542
6
+ top/kernel/gqa.py,sha256=Vw5LbN7Jmwo-wUzwOy1SBCOBjkOfM-FsFwkGmjd7C6M,48777
7
+ top/kernel/mamba_chunk_scan.py,sha256=e7Yr-p_fon2JQDoVyg2e-Zp_GWB-4tRGzlQ3Kpgt4LY,15134
8
+ top/kernel/mamba_chunk_state.py,sha256=5nrHr1ltCr4eN22MYD23XDey6AqIwn12Pp0qJbL8Ixk,10973
9
+ top/kernel/mha.py,sha256=OHrn7UnS7cmnRWfHbvhuAiPXYNzsDrgybuJugLzCHho,41561
10
+ top/kernel/mla.py,sha256=2-GNuH98Gm6kDVwvbMXzJWdH9qnmChpnt7TyA8EzcxA,14664
11
+ top/kernel/sparse_mla.py,sha256=r1Ld2BNBwQndwQCEQ7Au3oTLCKqFAUG2ltLc2NrDC9U,23032
12
+ top/kernel/linear_attention/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ top/kernel/linear_attention/linear_attn.py,sha256=tR67X8VYmGMjvFbpDcS8_5ok0_5wfnpZRG33URberuE,20593
14
+ top/utils/__init__.py,sha256=pG-YPVG0gJIJ6s4xcAz9S_CnUxpUcz33wl9eNUSgxGk,20
15
+ top/utils/utils.py,sha256=w1a4muz4sIvYqn9fhQw9JdrPloGRnBLcf98vnToXBBs,1837
16
+ tileops-0.0.1.dev1.dist-info/METADATA,sha256=r6njfd_aXqM6NhPWgw7LKzDmG_h2KJ4dwFS_pvCWNlg,795
17
+ tileops-0.0.1.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ tileops-0.0.1.dev1.dist-info/top_level.txt,sha256=994pR8ZMtkNeFfsr7zWdHtX2NWsq67eyBTXjdykE5ts,4
19
+ tileops-0.0.1.dev1.dist-info/RECORD,,
top/__init__.py CHANGED
@@ -1,18 +1,16 @@
1
- # Copyright (c) Tile-AI.
2
- # Licensed under the MIT License.
3
-
4
1
  from .kernel.mha import MHAKernel, MHADecodeKernel
5
2
  from .kernel.mla import MLAKernel
6
3
  from .kernel.gqa import GQAKernel, GQADecodeKernel
7
4
  from .kernel.mamba_chunk_scan import MambaChunkScanKernel
8
5
  from .kernel.mamba_chunk_state import MambaChunkStateKernel
9
6
  from .kernel.blocksparse_attention import BlockSparseAttentionKernel
10
- from .kernel.linear_attention.linear_attn import LinearAttentionFusedChunkKernel, LinearAttentionFusedRecurrentKernel
7
+ # from .kernel.linear_attention.linear_attn import LinearAttentionFusedChunkKernel, LinearAttentionFusedRecurrentKernel
11
8
  from .kernel.bitnet import Bitnet_158_int8xint2_kernel
9
+ from .kernel.sparse_mla import SparseMLAKernel
12
10
 
13
11
  __all__ = [
14
12
  "MHAKernel", "MHADecodeKernel", "MLAKernel", "GQAKernel", "GQADecodeKernel",
15
13
  "MambaChunkScanKernel", "MambaChunkStateKernel", "BlockSparseAttentionKernel",
16
- "LinearAttentionFusedChunkKernel", "LinearAttentionFusedRecurrentKernel",
17
- "Bitnet_158_int8xint2_kernel"
14
+ # "LinearAttentionFusedChunkKernel", "LinearAttentionFusedRecurrentKernel",
15
+ "Bitnet_158_int8xint2_kernel", "SparseMLAKernel"
18
16
  ]
top/kernel/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ from .mha import MHAKernel, MHADecodeKernel
2
+ from .mla import MLAKernel
3
+ from .gqa import GQAKernel, GQADecodeKernel
4
+ from .mamba_chunk_scan import MambaChunkScanKernel
5
+ from .mamba_chunk_state import MambaChunkStateKernel
6
+ from .blocksparse_attention import BlockSparseAttentionKernel
7
+ # from .linear_attention.linear_attn import LinearAttentionFusedChunkKernel, LinearAttentionFusedRecurrentKernel
8
+ from .bitnet import Bitnet_158_int8xint2_kernel
9
+
10
+ __all__ = [
11
+ "MHAKernel", "MHADecodeKernel", "MLAKernel", "GQAKernel", "GQADecodeKernel",
12
+ "MambaChunkScanKernel", "MambaChunkStateKernel", "BlockSparseAttentionKernel",
13
+ # "LinearAttentionFusedChunkKernel", "LinearAttentionFusedRecurrentKernel",
14
+ "Bitnet_158_int8xint2_kernel"
15
+ ]
top/kernel/bitnet.py ADDED
@@ -0,0 +1,490 @@
1
+ import torch
2
+ import torch.backends
3
+ import tilelang
4
+ import tilelang.language as T
5
+ import torch.nn as nn
6
+ from tilelang import tvm as tvm
7
+ from tvm import DataType
8
+ from tilelang.intrinsics.mma_layout import (
9
+ make_mma_swizzle_layout as make_swizzle_layout,)
10
+ import numpy as np
11
+
12
+ from tilelang.intrinsics.mma_macro_generator import (
13
+ INT4TensorCoreIntrinEmitter,)
14
+ from tilelang.transform import simplify_prim_func
15
+
16
+ try:
17
+ from tilelang.intrinsics.utils import index_to_coordinates
18
+ except ImportError:
19
+ from tilelang.language.utils import index_to_coordinates
20
+
21
+ decode_i2s_to_i8s = """template <typename T1, typename T2>
22
+ __device__ void decode_i2s_to_i8s(T1 *_i2b, T2 *_i8s, const int N = 16)
23
+ {
24
+ // convert 8 int2b_t to 8 int8b_t -> 2 int32
25
+ uint *i8s = reinterpret_cast<uint *>(_i8s);
26
+
27
+ // i2b = {e7,e6,e5,e4,e3,e2,e1,e0}
28
+ // also require interleave {e7,e3,e6,e2,e5,e1,e4,e0}
29
+ uint const i2b = *reinterpret_cast<uint *>(_i2b);
30
+
31
+ // First, we extract the i4s and construct an intermediate fp16 number.
32
+ static constexpr uint immLut = (0xf0 & 0xcc) | 0xaa; // 0b11101010
33
+ static constexpr uint BOTTOM_MASK = 0x03030303; // 0xf -> 0b11 select 0,3
34
+ static constexpr uint I8s_MAGIC_NUM = 0x00000000; // 1024
35
+ static constexpr uint MEDIAN_NUM = 0x02020202;
36
+ #pragma unroll
37
+ for (int i = 0; i < (N / 4); i++)
38
+ {
39
+ asm volatile("lop3.b32 %0, %1, %2, %3, %4;\\n"
40
+ : "=r"(i8s[i])
41
+ : "r"(i2b >> (2 * i)), "n"(BOTTOM_MASK), "n"(I8s_MAGIC_NUM), "n"(immLut));
42
+ i8s[i] = __vsub4(i8s[i], MEDIAN_NUM);
43
+ }
44
+ }
45
+ template <typename T1, typename T2>
46
+ __device__ void decode_i2u_to_i8s(T1 *_i2b, T2 *_i8s, const int N = 16)
47
+ {
48
+ // convert 8 int2b_t to 8 int8b_t -> 2 int32
49
+ uint *i8s = reinterpret_cast<uint *>(_i8s);
50
+
51
+ // i2b = {e7,e6,e5,e4,e3,e2,e1,e0}
52
+ // also require interleave {e7,e3,e6,e2,e5,e1,e4,e0}
53
+ uint const i2b = *reinterpret_cast<uint *>(_i2b);
54
+
55
+ // First, we extract the i4s and construct an intermediate fp16 number.
56
+ static constexpr uint immLut = (0xf0 & 0xcc) | 0xaa; // 0b11101010
57
+ static constexpr uint BOTTOM_MASK = 0x03030303; // 0xf -> 0b11 select 0,3
58
+ static constexpr uint I8s_MAGIC_NUM = 0x00000000; // 1024
59
+
60
+ #pragma unroll
61
+ for (int i = 0; i < (N / 4); i++)
62
+ {
63
+ asm volatile("lop3.b32 %0, %1, %2, %3, %4;\\n"
64
+ : "=r"(i8s[i])
65
+ : "r"(i2b >> (2 * i)), "n"(BOTTOM_MASK), "n"(I8s_MAGIC_NUM), "n"(immLut));
66
+ }
67
+ }
68
+ """
69
+
70
+
71
+ @simplify_prim_func
72
+ def _bitnet_158_int8xint2_prefill(
73
+ M,
74
+ N,
75
+ K,
76
+ in_dtype,
77
+ out_dtype,
78
+ accum_dtype,
79
+ fast_decoding=True,
80
+ block_row_warps=2,
81
+ block_col_warps=2,
82
+ warp_row_tiles=32,
83
+ warp_col_tiles=32,
84
+ chunk=64,
85
+ ):
86
+ assert in_dtype in [
87
+ "float16",
88
+ "int8",
89
+ ], "Currently only float16 and int8 are supported"
90
+ assert out_dtype in [
91
+ "float16",
92
+ "float32",
93
+ "int32",
94
+ ], "Currently only float16, float32 and int32 are supported"
95
+
96
+ micro_size_x = micro_size_y = micro_size_k = 16
97
+
98
+ if accum_dtype == "int32":
99
+ micro_size_k = 32
100
+
101
+ num_elems_per_byte = 4
102
+ MAX_TRANSACTION_SIZE_IN_BITS = 128
103
+ local_size = MAX_TRANSACTION_SIZE_IN_BITS // DataType(in_dtype).bits
104
+ local_size_compressed = local_size // num_elems_per_byte
105
+
106
+ shared_scope = "shared.dyn"
107
+ storage_dtype = "int8"
108
+
109
+ # Pipeline Stage
110
+ stage = 2
111
+
112
+ block_M = block_row_warps * warp_row_tiles
113
+ block_N = block_col_warps * warp_col_tiles
114
+ block_K = chunk
115
+
116
+ A_shape = (M, K) # int8 storage represents int4*2
117
+ B_shape = (N, K // num_elems_per_byte) # int8 storage represents int4*2
118
+ A_shared_shape = (block_M, block_K)
119
+ B_shared_shape = (block_N, block_K // num_elems_per_byte)
120
+ B_dequantize_shared_shape = (block_N, block_K)
121
+ C_shared_shape = (
122
+ block_M // micro_size_x,
123
+ block_N // micro_size_y,
124
+ micro_size_x,
125
+ micro_size_y,
126
+ )
127
+
128
+ warp_size = 32
129
+ threads = warp_size * (block_row_warps * block_col_warps)
130
+ fragement_size_a = (micro_size_x * micro_size_k) // warp_size
131
+ fragement_size_b = (micro_size_y * micro_size_k) // warp_size
132
+ fragement_size_c = (micro_size_x * micro_size_y) // warp_size
133
+ warp_rows = warp_row_tiles // micro_size_x
134
+ warp_cols = warp_col_tiles // micro_size_y
135
+
136
+ # MMA Wrapper to Auto Generate Code for MMA
137
+ mma_emitter = INT4TensorCoreIntrinEmitter(
138
+ a_dtype=in_dtype,
139
+ b_dtype=in_dtype,
140
+ accum_dtype=accum_dtype,
141
+ a_transposed=False,
142
+ b_transposed=True,
143
+ block_row_warps=block_row_warps,
144
+ block_col_warps=block_col_warps,
145
+ warp_row_tiles=warp_row_tiles,
146
+ warp_col_tiles=warp_col_tiles,
147
+ chunk=chunk,
148
+ )
149
+
150
+ @T.prim_func
151
+ def _bitnet_158_int8xint2_prefill_main(
152
+ A: T.Buffer(A_shape, in_dtype),
153
+ B: T.Buffer(B_shape, storage_dtype),
154
+ C: T.Buffer((M, N), out_dtype),
155
+ ):
156
+ with T.Kernel(
157
+ T.ceildiv(N, block_N),
158
+ T.ceildiv(M, block_M),
159
+ threads=threads,
160
+ prelude=decode_i2s_to_i8s,
161
+ ) as (bx, by):
162
+
163
+ A_shared = T.alloc_shared(A_shared_shape, in_dtype, scope=shared_scope)
164
+ B_shared = T.alloc_shared(B_shared_shape, storage_dtype, scope=shared_scope)
165
+ B_dequantize_shared = T.alloc_shared(
166
+ B_dequantize_shared_shape, in_dtype, scope=shared_scope)
167
+ C_shared = T.alloc_shared(C_shared_shape, out_dtype, scope=shared_scope)
168
+ A_frag = T.alloc_local((warp_rows * fragement_size_a), in_dtype)
169
+ B_frag = T.alloc_local((warp_cols * fragement_size_b), in_dtype)
170
+ C_frag = T.alloc_local((warp_rows * warp_cols * fragement_size_c), accum_dtype)
171
+
172
+ B_local = T.alloc_local([local_size_compressed], storage_dtype)
173
+ B_dequantize_local = T.alloc_local([local_size], in_dtype)
174
+
175
+ thread_bindings = T.thread_binding(0, threads, "threadIdx.x")
176
+
177
+ T.annotate_layout({
178
+ A_shared: make_swizzle_layout(A_shared),
179
+ B_dequantize_shared: make_swizzle_layout(B_dequantize_shared),
180
+ })
181
+
182
+ # Improve L2 Cache
183
+ T.use_swizzle(panel_size=10)
184
+
185
+ T.clear(C_frag)
186
+
187
+ for ko in T.Pipelined((K // block_K), num_stages=stage):
188
+
189
+ # Load A into shared memory
190
+ for i, k in T.Parallel(block_M, block_K):
191
+ A_shared[i, k] = A[by * block_M + i, ko * block_K + k]
192
+
193
+ # Load B into shared memory
194
+ for j, k in T.Parallel(block_N, block_K // num_elems_per_byte):
195
+ B_shared[j, k] = B[bx * block_N + j, ko * (block_K // num_elems_per_byte) + k]
196
+
197
+ for i in T.serial(block_N * block_K // num_elems_per_byte //
198
+ (threads * local_size_compressed)):
199
+ for v in T.vectorized(0, local_size_compressed):
200
+ index = (
201
+ i * threads * local_size_compressed +
202
+ thread_bindings * local_size_compressed + v)
203
+ vi, vj = index_to_coordinates(index, B_shared_shape)
204
+ B_local[v] = B_shared[vi, vj]
205
+
206
+ T.call_extern(
207
+ "handle",
208
+ "decode_i2u_to_i8s",
209
+ T.address_of(B_local[0]),
210
+ T.address_of(B_dequantize_local[0]),
211
+ )
212
+
213
+ for v in T.vectorized(0, local_size):
214
+ index = (i * threads * local_size + thread_bindings * local_size + v)
215
+ vi, vj = index_to_coordinates(index, B_dequantize_shared_shape)
216
+ B_dequantize_shared[vi, vj] = B_dequantize_local[v]
217
+
218
+ for ki in T.serial(0, (block_K // micro_size_k)):
219
+
220
+ # Load A into fragment
221
+ mma_emitter.ldmatrix_a(
222
+ A_frag,
223
+ A_shared,
224
+ ki,
225
+ )
226
+
227
+ # Load B into fragment
228
+ mma_emitter.ldmatrix_b(
229
+ B_frag,
230
+ B_dequantize_shared,
231
+ ki,
232
+ )
233
+
234
+ # Perform Matrix Multiplication
235
+ mma_emitter.mma(A_frag, B_frag, C_frag)
236
+
237
+ # Perform STMatrix
238
+ mma_emitter.stmatrix(
239
+ C_frag,
240
+ C_shared,
241
+ )
242
+
243
+ # Store shared into global
244
+ for i, j in T.Parallel(block_M, block_N):
245
+ C[by * block_M + i, bx * block_N + j] = C_shared[
246
+ i // micro_size_x,
247
+ j // micro_size_y,
248
+ i % micro_size_x,
249
+ j % micro_size_y,
250
+ ]
251
+
252
+ return _bitnet_158_int8xint2_prefill_main
253
+
254
+
255
+ @simplify_prim_func
256
+ def _bitnet_158_int8xint2_decode(
257
+ M,
258
+ N,
259
+ K,
260
+ in_dtype,
261
+ out_dtype,
262
+ accum_dtype,
263
+ fast_decoding=True,
264
+ n_partition=4,
265
+ reduce_thread=32,
266
+ ):
267
+ assert in_dtype in [
268
+ "float16",
269
+ "int8",
270
+ ], "Currently only float16 and int8 are supported"
271
+ assert out_dtype in [
272
+ "float16",
273
+ "float32",
274
+ "int32",
275
+ ], "Currently only float16, float32 and int32 are supported"
276
+ storage_nbit = 8
277
+ num_bits = 2
278
+ A_shape = (M, K)
279
+ B_shape = (N, K // storage_nbit * num_bits)
280
+ C_shape = (M, N)
281
+
282
+ num_elems_per_byte = 4
283
+ MAX_TRANSACTION_SIZE_IN_BITS = 128
284
+ micro_size_k = MAX_TRANSACTION_SIZE_IN_BITS // DataType(in_dtype).bits
285
+ micro_size_k_compressed = micro_size_k // num_elems_per_byte
286
+ storage_dtype = "int8"
287
+ block_K = reduce_thread * micro_size_k
288
+
289
+ use_dp4a = True
290
+ dp4a_size = 4
291
+
292
+ @T.prim_func
293
+ def _bitnet_158_int8xint2_decode_kernel(
294
+ A: T.Buffer(A_shape, in_dtype),
295
+ B: T.Buffer(B_shape, storage_dtype),
296
+ C: T.Buffer(C_shape, out_dtype),
297
+ ):
298
+ with T.Kernel(
299
+ T.ceildiv(N, n_partition),
300
+ M,
301
+ threads=(reduce_thread, n_partition),
302
+ ) as (
303
+ bx,
304
+ by,
305
+ ):
306
+ A_local = T.alloc_local((micro_size_k,), in_dtype)
307
+ B_quant_local = T.alloc_local([micro_size_k_compressed], storage_dtype)
308
+ B_dequantize_local = T.alloc_local([micro_size_k], in_dtype)
309
+ accum_res = T.alloc_local((1,), accum_dtype)
310
+ reduced_accum_res = T.alloc_local((1,), accum_dtype)
311
+
312
+ kr = T.thread_binding(0, reduce_thread, thread="threadIdx.x")
313
+ ni = T.thread_binding(0, n_partition, thread="threadIdx.y")
314
+
315
+ T.import_source(decode_i2s_to_i8s)
316
+
317
+ T.clear(accum_res)
318
+ for ko in T.serial(T.ceildiv(K, block_K)):
319
+ for v in T.vectorized(micro_size_k):
320
+ A_local[v] = A[by, ko * block_K + kr * micro_size_k + v]
321
+
322
+ for v in T.vectorized(micro_size_k_compressed):
323
+ B_quant_local[v] = B[
324
+ bx * n_partition + ni,
325
+ ko * (reduce_thread * micro_size_k_compressed) +
326
+ kr * micro_size_k_compressed + v,
327
+ ]
328
+
329
+ T.call_extern(
330
+ "handle",
331
+ "decode_i2u_to_i8s",
332
+ T.address_of(B_quant_local[0]),
333
+ T.address_of(B_dequantize_local[0]),
334
+ )
335
+
336
+ if use_dp4a:
337
+ for ki in T.serial(micro_size_k // dp4a_size):
338
+ T.dp4a(
339
+ A_local[ki * dp4a_size],
340
+ B_dequantize_local[ki * dp4a_size],
341
+ accum_res[0],
342
+ )
343
+ else:
344
+ for ki in T.serial(micro_size_k):
345
+ accum_res[0] += A_local[ki] * B_dequantize_local[ki]
346
+
347
+ with T.attr(
348
+ T.comm_reducer(lambda x, y: x + y, [T.Cast(accum_dtype, 0)]),
349
+ "reduce_scope",
350
+ T.reinterpret(T.uint64(0), dtype="handle"),
351
+ ):
352
+ T.evaluate(
353
+ T.tvm_thread_allreduce(
354
+ T.uint32(1),
355
+ accum_res[0],
356
+ True,
357
+ reduced_accum_res[0],
358
+ kr,
359
+ dtype="handle",
360
+ ))
361
+ if kr == 0:
362
+ C[by, bx * n_partition + ni] = reduced_accum_res[0]
363
+
364
+ return _bitnet_158_int8xint2_decode_kernel
365
+
366
+
367
+ class Bitnet_158_int8xint2_kernel(nn.Module):
368
+
369
+ def __init__(self,
370
+ M,
371
+ N,
372
+ K,
373
+ in_dtype,
374
+ out_dtype,
375
+ accum_dtype,
376
+ fast_decoding=True):
377
+ super().__init__()
378
+ self.in_dtype = in_dtype
379
+ self.accum_dtype = accum_dtype
380
+ self.prefill_program = _bitnet_158_int8xint2_prefill(M, N, K, in_dtype, out_dtype, accum_dtype, fast_decoding)
381
+ self.prefill_kernel = tilelang.compile(self.prefill_program)
382
+
383
+ self.decode_program = _bitnet_158_int8xint2_decode(M, N, K, in_dtype, out_dtype, accum_dtype, fast_decoding)
384
+ self.decode_kernel = tilelang.compile(self.decode_program)
385
+
386
+ @staticmethod
387
+ def general_compress(lowprecision_weight, source_bits=4, storage_dtype=np.int8):
388
+ elems_per_byte = 8 // source_bits
389
+ if lowprecision_weight.dtype == np.float16:
390
+ lowprecision_weight = lowprecision_weight.astype(dtype=np.int8)
391
+ int8_weight = np.zeros(
392
+ (
393
+ *lowprecision_weight.shape[:-1],
394
+ lowprecision_weight.shape[-1] // elems_per_byte,
395
+ ),
396
+ dtype=np.int8,
397
+ )
398
+ for j in range(lowprecision_weight.shape[-1] // elems_per_byte):
399
+ for k in range(elems_per_byte):
400
+ int8_weight[:, j] |= lowprecision_weight[:, j * elems_per_byte + k] << (source_bits * k)
401
+
402
+ return int8_weight.view(storage_dtype)
403
+
404
+
405
+ # interleave weight numpy implementation
406
+ @staticmethod
407
+ def interleave_weight(qweight, nbits=4, target_dtype="float16"):
408
+ assert target_dtype in ["float16", "int8"]
409
+ # reinterpret the data type of qweight to int32
410
+ qweight = qweight.view(np.int32)
411
+ new_qweight = np.zeros_like(qweight)
412
+ bits_stride = 8 if target_dtype == "int8" else 16
413
+ mask = (1 << nbits) - 1 # for 4bit the val is 0x0000000f
414
+ num_groups = 32 // bits_stride
415
+ elems_per_group = bits_stride // nbits
416
+ for i in range(num_groups):
417
+ for j in range(elems_per_group):
418
+ offset = i * elems_per_group + j
419
+ shift = (offset % num_groups) * bits_stride + (offset // num_groups) * nbits
420
+ new_qweight |= ((qweight >> (nbits * offset)) & mask) << shift
421
+
422
+ if nbits == 1 and target_dtype == "int8":
423
+ # special handling for 1b interleave
424
+ n16_weight = new_qweight & np.int32(0xF0F00F0F)
425
+ n16_weight |= ((new_qweight & np.int32(0x000000F0)) >> 4) << 16
426
+ n16_weight |= ((new_qweight & np.int32(0x0000F000)) >> 12) << 24
427
+ n16_weight |= ((new_qweight & np.int32(0x000F0000)) >> 16) << 4
428
+ n16_weight |= ((new_qweight & np.int32(0x0F000000)) >> 24) << 12
429
+ return n16_weight.view(np.int8)
430
+ elif nbits == 2 and target_dtype == "float16":
431
+ n8_weight = new_qweight & np.int32(0xFF0000FF)
432
+ n8_weight |= ((new_qweight & np.int32(0x0000FF00)) >> 8) << 16
433
+ n8_weight |= ((new_qweight & np.int32(0x00FF0000)) >> 16) << 8
434
+ return n8_weight.view(np.int8)
435
+ elif nbits == 1 and target_dtype == "float16":
436
+ n8_weight = new_qweight & 0xF000000F
437
+ n8_weight |= ((new_qweight & 0x000000F0) >> 4) << 8
438
+ n8_weight |= ((new_qweight & 0x00000F00) >> 8) << 16
439
+ n8_weight |= ((new_qweight & 0x0000F000) >> 12) << 24
440
+ n8_weight |= ((new_qweight & 0x000F0000) >> 16) << 4
441
+ n8_weight |= ((new_qweight & 0x00F00000) >> 20) << 12
442
+ n8_weight |= ((new_qweight & 0x0F000000) >> 24) << 20
443
+
444
+ return new_qweight.view(np.int8)
445
+
446
+ def get_B_int8(self, B):
447
+ qw = self.general_compress(B.cpu().numpy(), source_bits=2, storage_dtype=np.int8)
448
+ qw = self.interleave_weight(qw, 2, target_dtype=self.in_dtype)
449
+ qw = torch.from_numpy(qw).to(device="cuda")
450
+ return qw
451
+
452
+ def prefill(self, A, B, C):
453
+ src_code = self.prefill_kernel.get_kernel_source()
454
+ # src_code is the generated cuda source
455
+ assert src_code is not None
456
+ # print(src_code)
457
+ qw = self.get_B_int8(B)
458
+ self.prefill_kernel(A, qw, C)
459
+ return C
460
+
461
+ def decode(self, A, B, C):
462
+ src_code = self.decode_kernel.get_kernel_source()
463
+ # src_code is the generated cuda source
464
+ assert src_code is not None
465
+ # print(src_code)
466
+ qw = self.get_B_int8(B)
467
+ self.decode_kernel(A, qw, C)
468
+ return C
469
+
470
+ def profile(self, warmup=500):
471
+ pass
472
+ # latency = self.bwd_profiler.do_bench(warmup=warmup)
473
+ # return latency
474
+
475
+ def ref_program(self, A, B):
476
+ ref_c = torch.matmul(A.to(torch.float32), B.T.to(torch.float32)).to(getattr(torch, self.accum_dtype))
477
+ return ref_c
478
+
479
+ def check(self, A, B, C, mode):
480
+ if mode == "prefill":
481
+ C = self.prefill(A, B, C)
482
+ elif mode == "decode":
483
+ C = self.decode(A, B, C)
484
+ else:
485
+ raise ValueError("mode should be either 'prefill' or 'decode'")
486
+
487
+ ref_c = self.ref_program(A, B)
488
+ torch.testing.assert_close(C, ref_c, rtol=1e-2, atol=1e-2)
489
+ print(f"Bitnet_158_int8xint2 {mode} kernel check passed!")
490
+