bitsandbytes 0.50.0__py3-none-win_arm64.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.
- bitsandbytes/__init__.py +78 -0
- bitsandbytes/__main__.py +4 -0
- bitsandbytes/_ops.py +510 -0
- bitsandbytes/autograd/__init__.py +0 -0
- bitsandbytes/autograd/_functions.py +491 -0
- bitsandbytes/backends/__init__.py +0 -0
- bitsandbytes/backends/cpu/__init__.py +0 -0
- bitsandbytes/backends/cpu/ops.py +580 -0
- bitsandbytes/backends/cuda/__init__.py +0 -0
- bitsandbytes/backends/cuda/ops.py +1199 -0
- bitsandbytes/backends/default/__init__.py +0 -0
- bitsandbytes/backends/default/ops.py +632 -0
- bitsandbytes/backends/hpu/__init__.py +0 -0
- bitsandbytes/backends/hpu/ops.py +53 -0
- bitsandbytes/backends/mps/__init__.py +0 -0
- bitsandbytes/backends/mps/ops.py +277 -0
- bitsandbytes/backends/triton/__init__.py +0 -0
- bitsandbytes/backends/triton/kernels_4bit.py +577 -0
- bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -0
- bitsandbytes/backends/triton/kernels_optim.py +1177 -0
- bitsandbytes/backends/triton/ops.py +304 -0
- bitsandbytes/backends/utils.py +94 -0
- bitsandbytes/backends/xpu/__init__.py +0 -0
- bitsandbytes/backends/xpu/ops.py +305 -0
- bitsandbytes/cextension.py +405 -0
- bitsandbytes/consts.py +12 -0
- bitsandbytes/cuda_specs.py +111 -0
- bitsandbytes/diagnostics/__init__.py +0 -0
- bitsandbytes/diagnostics/cuda.py +193 -0
- bitsandbytes/diagnostics/main.py +134 -0
- bitsandbytes/diagnostics/utils.py +12 -0
- bitsandbytes/functional.py +1810 -0
- bitsandbytes/libbitsandbytes_cpu.dll +0 -0
- bitsandbytes/nn/__init__.py +19 -0
- bitsandbytes/nn/modules.py +1220 -0
- bitsandbytes/nn/parametrize.py +206 -0
- bitsandbytes/optim/__init__.py +22 -0
- bitsandbytes/optim/adagrad.py +187 -0
- bitsandbytes/optim/adam.py +346 -0
- bitsandbytes/optim/adamw.py +337 -0
- bitsandbytes/optim/ademamix.py +410 -0
- bitsandbytes/optim/lamb.py +190 -0
- bitsandbytes/optim/lars.py +259 -0
- bitsandbytes/optim/lion.py +266 -0
- bitsandbytes/optim/optimizer.py +756 -0
- bitsandbytes/optim/rmsprop.py +170 -0
- bitsandbytes/optim/sgd.py +152 -0
- bitsandbytes/py.typed +0 -0
- bitsandbytes/utils.py +208 -0
- bitsandbytes-0.50.0.dist-info/METADATA +288 -0
- bitsandbytes-0.50.0.dist-info/RECORD +54 -0
- bitsandbytes-0.50.0.dist-info/WHEEL +5 -0
- bitsandbytes-0.50.0.dist-info/licenses/LICENSE +21 -0
- bitsandbytes-0.50.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1199 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
import ctypes as ct
|
|
3
|
+
import functools
|
|
4
|
+
from math import prod
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from warnings import warn
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
|
|
10
|
+
from bitsandbytes.functional import CUBLAS_Context, _cuda_device_of, get_ptr
|
|
11
|
+
|
|
12
|
+
from ..._ops import register_kernel
|
|
13
|
+
from ...cextension import lib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _setup_ctypes(names, argtypes, restype=None):
|
|
17
|
+
for name in names:
|
|
18
|
+
fn = getattr(lib, name)
|
|
19
|
+
fn.argtypes = argtypes
|
|
20
|
+
fn.restype = restype
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# 4-bit/8-bit dequantize: (code, A, absmax, out, blocksize, numel, stream)
|
|
24
|
+
_setup_ctypes(
|
|
25
|
+
[f"cdequantize_blockwise_{d}_{q}" for d in ("fp32", "bf16", "fp16") for q in ("nf4", "fp4")]
|
|
26
|
+
+ [f"cdequantize_blockwise_{d}" for d in ("fp32", "bf16", "fp16")],
|
|
27
|
+
[ct.c_void_p] * 4 + [ct.c_int32, ct.c_int32, ct.c_void_p],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# 4-bit GEMM: (A, B, absmax, absmax_8bit, absmax_code, absmax_offset, out, bias, M, N, K, blocksize, quant_type, stream)
|
|
31
|
+
_setup_ctypes(
|
|
32
|
+
[f"cgemm_4bit_{d}" for d in ("bf16", "fp16", "fp32")],
|
|
33
|
+
[ct.c_void_p] * 8 + [ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_void_p],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# 4-bit GEMV: (m, n, k, A, B, absmax, code, out, lda, ldb, ldc, blocksize, stream)
|
|
37
|
+
_setup_ctypes(
|
|
38
|
+
[f"cgemm_4bit_inference_naive_{d}" for d in ("bf16", "fp16", "fp32")],
|
|
39
|
+
[ct.c_int32] * 3 + [ct.c_void_p] * 5 + [ct.c_int32] * 3 + [ct.c_int32, ct.c_void_p],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# int8 igemm: (ctx, m, n, k, A, B, C, rowscale, lda, ldb, ldc, stream) -> int32
|
|
43
|
+
_setup_ctypes(
|
|
44
|
+
["cigemmlt_32"],
|
|
45
|
+
[ct.c_void_p] + [ct.c_int32] * 3 + [ct.c_void_p] * 4 + [ct.c_int32] * 3 + [ct.c_void_p],
|
|
46
|
+
restype=ct.c_int32,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# int8 mm dequant: (A, row_stats, col_stats, out, bias, numRows, numCols, stream)
|
|
50
|
+
_setup_ctypes(
|
|
51
|
+
["cdequant_mm_int32_fp16"],
|
|
52
|
+
[ct.c_void_p] * 5 + [ct.c_int32, ct.c_int32, ct.c_void_p],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# int8 vectorwise quant: (A, out, row_stats, threshold, rows, cols, stream)
|
|
56
|
+
_setup_ctypes(
|
|
57
|
+
["cint8_vector_quant"],
|
|
58
|
+
[ct.c_void_p] * 3 + [ct.c_float, ct.c_int32, ct.c_int32, ct.c_void_p],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# 4-bit/8-bit blockwise quantize: (code, A, absmax, out, blocksize, n)
|
|
62
|
+
_setup_ctypes(
|
|
63
|
+
[f"cquantize_blockwise_{d}_{q}" for d in ("fp32", "bf16", "fp16") for q in ("nf4", "fp4")]
|
|
64
|
+
+ [f"cquantize_blockwise_{d}" for d in ("fp32", "bf16", "fp16")],
|
|
65
|
+
[ct.c_void_p] * 4 + [ct.c_int32, ct.c_int32],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
_get_raw_stream = torch._C._cuda_getCurrentRawStream
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@functools.cache
|
|
73
|
+
def _gpu_dispatch_props(device_index):
|
|
74
|
+
props = torch.cuda.get_device_properties(device_index)
|
|
75
|
+
return props.multi_processor_count, props.major, props.minor
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@register_kernel("bitsandbytes::int8_linear_matmul", "cuda")
|
|
79
|
+
def _(A: torch.Tensor, B: torch.Tensor):
|
|
80
|
+
out = torch.empty((*A.shape[:-1], B.shape[0]), device=A.device, dtype=torch.int32)
|
|
81
|
+
return _int8_linear_matmul_impl(A, B, out)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@register_kernel("bitsandbytes::int8_linear_matmul.out", "cuda")
|
|
85
|
+
def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
|
|
86
|
+
_int8_linear_matmul_impl(A, B, out)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _int8_linear_matmul_impl(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
|
|
90
|
+
A, B = B, A
|
|
91
|
+
|
|
92
|
+
shapeA = A.shape
|
|
93
|
+
shapeB = B.shape
|
|
94
|
+
|
|
95
|
+
if A.dtype != torch.int8:
|
|
96
|
+
raise ValueError("B must be int8")
|
|
97
|
+
if B.dtype != torch.int8:
|
|
98
|
+
raise ValueError("A must be int8")
|
|
99
|
+
if A.ndim != 2:
|
|
100
|
+
raise ValueError("Only two dimensional matrices are supported for argument B")
|
|
101
|
+
if B.ndim not in (2, 3):
|
|
102
|
+
raise ValueError("Only two or three dimensional matrices are supported for argument A")
|
|
103
|
+
if prod(shapeB) <= 0:
|
|
104
|
+
raise ValueError(f"Input tensor dimensions need to be > 0: {shapeB}")
|
|
105
|
+
if out.dtype != torch.int32:
|
|
106
|
+
raise ValueError(f"out must be int32, got {out.dtype}")
|
|
107
|
+
|
|
108
|
+
shapeC = (*shapeB[:-1], shapeA[0])
|
|
109
|
+
if out.shape != shapeC:
|
|
110
|
+
raise ValueError(f"Output shape {out.shape} does not match expected shape {shapeC}")
|
|
111
|
+
|
|
112
|
+
k, m = shapeA
|
|
113
|
+
n = prod(shapeB[:-1])
|
|
114
|
+
lda = shapeA[-1] # Weights (outputs, inputs)
|
|
115
|
+
ldb = shapeB[-1] # Activations (batch, tokens, inputs)
|
|
116
|
+
ldc = shapeC[-1] # Output (batch, tokens, outputs)
|
|
117
|
+
|
|
118
|
+
if lda != ldb:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"int8_linear_matmul only supports B^T @ A. Inner dimensions do not match: B @ A = {shapeB} @ {shapeA}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# cuBLASLt does not support int8 matmul with inner dimensions that are not divisible by 4.
|
|
124
|
+
# We'll fall back to a slower fp32 calculation in this circumstance.
|
|
125
|
+
# Fortunately, this should not be very common.
|
|
126
|
+
if lda % 4 != 0:
|
|
127
|
+
result = torch.matmul(B.float(), A.float().t()).to(torch.int32)
|
|
128
|
+
return out.copy_(result)
|
|
129
|
+
|
|
130
|
+
with _cuda_device_of(A):
|
|
131
|
+
ctx = CUBLAS_Context.get_instance().get_context(A.device)
|
|
132
|
+
has_error = lib.cigemmlt_32(
|
|
133
|
+
ctx,
|
|
134
|
+
m,
|
|
135
|
+
n,
|
|
136
|
+
k,
|
|
137
|
+
A.data_ptr(),
|
|
138
|
+
B.data_ptr(),
|
|
139
|
+
out.data_ptr(),
|
|
140
|
+
None,
|
|
141
|
+
lda,
|
|
142
|
+
ldb,
|
|
143
|
+
ldc,
|
|
144
|
+
_get_raw_stream(A.device.index),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if has_error:
|
|
148
|
+
if has_error == 100:
|
|
149
|
+
# `ERR_NOT_IMPLEMENTED` is defined as 100 in `ops.cu`. The HIP backend
|
|
150
|
+
# also returns this when no usable hipBLASLt algo exists for the shape
|
|
151
|
+
# (seen on MI300X for some small-n int8 gemms). Fall back to fp32 — same
|
|
152
|
+
# path used for the `lda % 4 != 0` case above.
|
|
153
|
+
import warnings
|
|
154
|
+
|
|
155
|
+
warnings.warn(
|
|
156
|
+
f"int8_linear_matmul has no usable (hip|cu)blasLt algo for shape "
|
|
157
|
+
f"{shapeA=} {shapeB=}; falling back to fp32 matmul.",
|
|
158
|
+
RuntimeWarning,
|
|
159
|
+
stacklevel=2,
|
|
160
|
+
)
|
|
161
|
+
result = torch.matmul(B.float(), A.float().t()).to(torch.int32)
|
|
162
|
+
return out.copy_(result)
|
|
163
|
+
else:
|
|
164
|
+
raise RuntimeError(
|
|
165
|
+
f"cublasLt ran into an error!\n\t{shapeA=}, {shapeB=}, {shapeC=}\n\t{(lda, ldb, ldc)=}\n\t{(m, n, k)=}"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return out
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@register_kernel("bitsandbytes::int8_mm_dequant", "cuda")
|
|
172
|
+
def _(
|
|
173
|
+
A: torch.Tensor,
|
|
174
|
+
row_stats: torch.Tensor,
|
|
175
|
+
col_stats: torch.Tensor,
|
|
176
|
+
dtype: Optional[torch.dtype] = None,
|
|
177
|
+
bias: Optional[torch.Tensor] = None,
|
|
178
|
+
) -> torch.Tensor:
|
|
179
|
+
if A.dtype != torch.int32:
|
|
180
|
+
raise ValueError(f"A must be int32, got {A.dtype}")
|
|
181
|
+
if row_stats.dtype != torch.float32:
|
|
182
|
+
raise ValueError(f"row_stats must be float32, got {row_stats.dtype}")
|
|
183
|
+
if col_stats.dtype != torch.float32:
|
|
184
|
+
raise ValueError(f"col_stats must be float32, got {col_stats.dtype}")
|
|
185
|
+
|
|
186
|
+
# Note: cuda kernel only currently supports fp16 output.
|
|
187
|
+
# We'll later cast to desired dtype if needed.
|
|
188
|
+
out = torch.empty_like(A, dtype=torch.float16)
|
|
189
|
+
|
|
190
|
+
# Note: fused bias in the kernel is only supported for fp16
|
|
191
|
+
# TODO(matthewdouglas): Consider supporting bf16 fused bias
|
|
192
|
+
bias_ptr = bias.data_ptr() if bias is not None and bias.dtype == torch.float16 else None
|
|
193
|
+
|
|
194
|
+
with _cuda_device_of(A):
|
|
195
|
+
lib.cdequant_mm_int32_fp16(
|
|
196
|
+
A.data_ptr(),
|
|
197
|
+
row_stats.data_ptr(),
|
|
198
|
+
col_stats.data_ptr(),
|
|
199
|
+
out.data_ptr(),
|
|
200
|
+
bias_ptr,
|
|
201
|
+
A.numel() // A.shape[-1],
|
|
202
|
+
A.shape[-1],
|
|
203
|
+
_get_raw_stream(A.device.index),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Add bias separately if not fused in kernel
|
|
207
|
+
if bias is not None and bias.dtype != torch.float16:
|
|
208
|
+
out.add_(bias)
|
|
209
|
+
|
|
210
|
+
return out.to(dtype or torch.float16)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@register_kernel("bitsandbytes::int8_vectorwise_quant", "cuda")
|
|
214
|
+
def _(A: torch.Tensor, threshold=0.0):
|
|
215
|
+
if A.dtype != torch.float16:
|
|
216
|
+
raise ValueError(f"A must be float16, got {A.dtype}")
|
|
217
|
+
if threshold < 0.0:
|
|
218
|
+
raise ValueError("threshold must be non-negative")
|
|
219
|
+
|
|
220
|
+
rows = A.numel() // A.shape[-1]
|
|
221
|
+
cols = A.shape[-1]
|
|
222
|
+
|
|
223
|
+
row_stats = torch.empty(rows, device=A.device, dtype=torch.float32)
|
|
224
|
+
out_row = torch.empty(A.shape, device=A.device, dtype=torch.int8)
|
|
225
|
+
|
|
226
|
+
outlier_cols = None
|
|
227
|
+
|
|
228
|
+
if threshold > 0.0:
|
|
229
|
+
# TODO we could improve perf of this
|
|
230
|
+
outliers = A.abs() >= threshold
|
|
231
|
+
|
|
232
|
+
if outliers.any():
|
|
233
|
+
outlier_cols = torch.argwhere(outliers.any(dim=0)).view(-1)
|
|
234
|
+
else:
|
|
235
|
+
# Needed for torch.compile support.
|
|
236
|
+
outlier_cols = torch.empty(0, device=A.device, dtype=torch.int64)
|
|
237
|
+
|
|
238
|
+
with _cuda_device_of(A):
|
|
239
|
+
lib.cint8_vector_quant(
|
|
240
|
+
A.data_ptr(),
|
|
241
|
+
out_row.data_ptr(),
|
|
242
|
+
row_stats.data_ptr(),
|
|
243
|
+
threshold,
|
|
244
|
+
rows,
|
|
245
|
+
cols,
|
|
246
|
+
_get_raw_stream(A.device.index),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Zero out values from outlier columns across all rows.
|
|
250
|
+
# The kernel will handle this for outliers themselves, so we can optimize for rows=1.
|
|
251
|
+
if rows > 1 and outlier_cols is not None:
|
|
252
|
+
out_row[:, outlier_cols] = 0
|
|
253
|
+
|
|
254
|
+
return out_row, row_stats, outlier_cols
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@register_kernel("bitsandbytes::int8_double_quant", "cuda")
|
|
258
|
+
def _(
|
|
259
|
+
A: torch.Tensor,
|
|
260
|
+
threshold=0.0,
|
|
261
|
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
|
|
262
|
+
# Use CUDA kernel for rowwise quant and outlier column detection
|
|
263
|
+
quant_row, row_stats, outlier_cols = torch.ops.bitsandbytes.int8_vectorwise_quant.default(
|
|
264
|
+
A,
|
|
265
|
+
threshold=threshold,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# PyTorch impl for colwise
|
|
269
|
+
col_stats, outlier_mask = _get_col_absmax(A, threshold=threshold)
|
|
270
|
+
if threshold > 0.0 and outlier_mask is not None:
|
|
271
|
+
A = A.masked_fill(outlier_mask, 0.0)
|
|
272
|
+
quant_col = torch.round(A.mul(127.0) / col_stats.unsqueeze(0)).to(torch.int8)
|
|
273
|
+
|
|
274
|
+
return quant_row, quant_col, row_stats, col_stats.flatten().float(), outlier_cols
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _get_col_absmax(
|
|
278
|
+
A: torch.Tensor,
|
|
279
|
+
threshold=0.0,
|
|
280
|
+
) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
|
|
281
|
+
if not A.is_floating_point():
|
|
282
|
+
raise ValueError(f"A must be a floating point tensor, got {A.dtype}")
|
|
283
|
+
|
|
284
|
+
outlier_mask = None
|
|
285
|
+
|
|
286
|
+
absA = A.abs().view(-1, A.shape[-1])
|
|
287
|
+
|
|
288
|
+
if threshold > 0.0:
|
|
289
|
+
# Filter outliers from stats when enabled
|
|
290
|
+
outlier_mask = absA >= threshold
|
|
291
|
+
absA.masked_fill_(outlier_mask, 0.0)
|
|
292
|
+
|
|
293
|
+
# shape [cols]; unsqueeze(0) gives [1,cols]
|
|
294
|
+
col_stats = absA.amax(dim=0, keepdim=False).float()
|
|
295
|
+
|
|
296
|
+
return col_stats, outlier_mask
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@register_kernel("bitsandbytes::quantize_blockwise", "cuda")
|
|
300
|
+
def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
|
|
301
|
+
A = A.contiguous()
|
|
302
|
+
|
|
303
|
+
if code.dtype != torch.float32:
|
|
304
|
+
raise ValueError(f"code must be float32, got {code.dtype}")
|
|
305
|
+
if blocksize not in (64, 128, 256, 512, 1024, 2048, 4096):
|
|
306
|
+
raise ValueError(f"invalid blocksize {blocksize}")
|
|
307
|
+
|
|
308
|
+
n = A.numel()
|
|
309
|
+
blocks = -(n // -blocksize)
|
|
310
|
+
absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
|
|
311
|
+
out = torch.empty_like(A, dtype=torch.uint8)
|
|
312
|
+
|
|
313
|
+
if A.dtype == torch.float32:
|
|
314
|
+
fn = lib.cquantize_blockwise_fp32
|
|
315
|
+
elif A.dtype == torch.float16:
|
|
316
|
+
fn = lib.cquantize_blockwise_fp16
|
|
317
|
+
elif A.dtype == torch.bfloat16:
|
|
318
|
+
fn = lib.cquantize_blockwise_bf16
|
|
319
|
+
else:
|
|
320
|
+
raise ValueError(f"Blockwise quantization only supports 16/32-bit floats, but got {A.dtype}")
|
|
321
|
+
|
|
322
|
+
with _cuda_device_of(A):
|
|
323
|
+
fn(
|
|
324
|
+
code.data_ptr(),
|
|
325
|
+
A.data_ptr(),
|
|
326
|
+
absmax.data_ptr(),
|
|
327
|
+
out.data_ptr(),
|
|
328
|
+
blocksize,
|
|
329
|
+
n,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return out, absmax
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@register_kernel("bitsandbytes::dequantize_blockwise", "cuda")
|
|
336
|
+
def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
|
|
337
|
+
out = torch.empty_like(A, dtype=dtype)
|
|
338
|
+
_dequantize_blockwise_impl(A, absmax, code, blocksize, dtype, out=out)
|
|
339
|
+
return out
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@register_kernel("bitsandbytes::dequantize_blockwise.out", "cuda")
|
|
343
|
+
def _(
|
|
344
|
+
A: torch.Tensor,
|
|
345
|
+
absmax: torch.Tensor,
|
|
346
|
+
code: torch.Tensor,
|
|
347
|
+
blocksize: int,
|
|
348
|
+
dtype: torch.dtype,
|
|
349
|
+
out: torch.Tensor,
|
|
350
|
+
) -> None:
|
|
351
|
+
if out.dtype != dtype:
|
|
352
|
+
raise ValueError(f"Expected out.dtype == {dtype}, got {out.dtype}")
|
|
353
|
+
if out.shape != A.shape:
|
|
354
|
+
raise ValueError(f"Expected out.shape == {A.shape}, got {out.shape}")
|
|
355
|
+
_dequantize_blockwise_impl(A, absmax, code, blocksize, dtype, out=out)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _dequantize_blockwise_impl(
|
|
359
|
+
A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype, out: torch.Tensor
|
|
360
|
+
) -> None:
|
|
361
|
+
A = A.contiguous()
|
|
362
|
+
|
|
363
|
+
if dtype == torch.float32:
|
|
364
|
+
fn = lib.cdequantize_blockwise_fp32
|
|
365
|
+
elif dtype == torch.float16:
|
|
366
|
+
fn = lib.cdequantize_blockwise_fp16
|
|
367
|
+
elif dtype == torch.bfloat16:
|
|
368
|
+
fn = lib.cdequantize_blockwise_bf16
|
|
369
|
+
else:
|
|
370
|
+
raise ValueError(f"Blockwise dequantization only supports 16/32-bit floats, but got {dtype}")
|
|
371
|
+
|
|
372
|
+
with _cuda_device_of(A):
|
|
373
|
+
fn(
|
|
374
|
+
code.data_ptr(),
|
|
375
|
+
A.data_ptr(),
|
|
376
|
+
absmax.data_ptr(),
|
|
377
|
+
out.data_ptr(),
|
|
378
|
+
blocksize,
|
|
379
|
+
A.numel(),
|
|
380
|
+
_get_raw_stream(A.device.index),
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
@register_kernel("bitsandbytes::quantize_4bit", "cuda")
|
|
385
|
+
def _(
|
|
386
|
+
A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
|
|
387
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
388
|
+
A = A.contiguous()
|
|
389
|
+
n = A.numel()
|
|
390
|
+
blocks = -(n // -blocksize)
|
|
391
|
+
absmax = torch.empty((blocks,), device=A.device, dtype=torch.float32)
|
|
392
|
+
out = torch.empty(((n + 1) // (quant_storage.itemsize * 2), 1), device=A.device, dtype=quant_storage)
|
|
393
|
+
|
|
394
|
+
if A.dtype == torch.bfloat16:
|
|
395
|
+
if quant_type == "fp4":
|
|
396
|
+
fn = lib.cquantize_blockwise_bf16_fp4
|
|
397
|
+
else:
|
|
398
|
+
fn = lib.cquantize_blockwise_bf16_nf4
|
|
399
|
+
elif A.dtype == torch.float16:
|
|
400
|
+
if quant_type == "fp4":
|
|
401
|
+
fn = lib.cquantize_blockwise_fp16_fp4
|
|
402
|
+
else:
|
|
403
|
+
fn = lib.cquantize_blockwise_fp16_nf4
|
|
404
|
+
elif A.dtype == torch.float32:
|
|
405
|
+
if quant_type == "fp4":
|
|
406
|
+
fn = lib.cquantize_blockwise_fp32_fp4
|
|
407
|
+
else:
|
|
408
|
+
fn = lib.cquantize_blockwise_fp32_nf4
|
|
409
|
+
|
|
410
|
+
with _cuda_device_of(A):
|
|
411
|
+
fn(
|
|
412
|
+
None,
|
|
413
|
+
A.data_ptr(),
|
|
414
|
+
absmax.data_ptr(),
|
|
415
|
+
out.data_ptr(),
|
|
416
|
+
blocksize,
|
|
417
|
+
n,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
return out, absmax
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
@register_kernel("bitsandbytes::dequantize_4bit", "cuda")
|
|
424
|
+
def _(
|
|
425
|
+
A: torch.Tensor,
|
|
426
|
+
absmax: torch.Tensor,
|
|
427
|
+
blocksize: int,
|
|
428
|
+
quant_type: str,
|
|
429
|
+
shape: Sequence[int],
|
|
430
|
+
dtype: torch.dtype,
|
|
431
|
+
) -> torch.Tensor:
|
|
432
|
+
out = torch.empty(shape, dtype=dtype, device=A.device)
|
|
433
|
+
_dequantize_4bit_impl(A, absmax, blocksize, quant_type, dtype, out=out)
|
|
434
|
+
return out
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@register_kernel("bitsandbytes::dequantize_4bit.out", "cuda")
|
|
438
|
+
def _(
|
|
439
|
+
A: torch.Tensor,
|
|
440
|
+
absmax: torch.Tensor,
|
|
441
|
+
blocksize: int,
|
|
442
|
+
quant_type: str,
|
|
443
|
+
shape: Sequence[int],
|
|
444
|
+
dtype: torch.dtype,
|
|
445
|
+
out: torch.Tensor,
|
|
446
|
+
) -> None:
|
|
447
|
+
if out.shape != tuple(shape):
|
|
448
|
+
raise ValueError(f"Expected out.shape == {shape}, got {out.shape}")
|
|
449
|
+
if out.dtype != dtype:
|
|
450
|
+
raise ValueError(f"Expected out.dtype == {dtype}, got {out.dtype}")
|
|
451
|
+
_dequantize_4bit_impl(A, absmax, blocksize, quant_type, dtype, out=out)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _dequantize_4bit_impl(
|
|
455
|
+
A: torch.Tensor,
|
|
456
|
+
absmax: torch.Tensor,
|
|
457
|
+
blocksize: int,
|
|
458
|
+
quant_type: str,
|
|
459
|
+
dtype: torch.dtype,
|
|
460
|
+
out: torch.Tensor,
|
|
461
|
+
) -> None:
|
|
462
|
+
A = A.contiguous()
|
|
463
|
+
|
|
464
|
+
if dtype == torch.bfloat16:
|
|
465
|
+
if quant_type == "fp4":
|
|
466
|
+
fn = lib.cdequantize_blockwise_bf16_fp4
|
|
467
|
+
else:
|
|
468
|
+
fn = lib.cdequantize_blockwise_bf16_nf4
|
|
469
|
+
elif dtype == torch.float16:
|
|
470
|
+
if quant_type == "fp4":
|
|
471
|
+
fn = lib.cdequantize_blockwise_fp16_fp4
|
|
472
|
+
else:
|
|
473
|
+
fn = lib.cdequantize_blockwise_fp16_nf4
|
|
474
|
+
elif dtype == torch.float32:
|
|
475
|
+
if quant_type == "fp4":
|
|
476
|
+
fn = lib.cdequantize_blockwise_fp32_fp4
|
|
477
|
+
else:
|
|
478
|
+
fn = lib.cdequantize_blockwise_fp32_nf4
|
|
479
|
+
else:
|
|
480
|
+
raise ValueError(f"Blockwise 4bit dequantization only supports 16/32-bit floats, but got {dtype}")
|
|
481
|
+
|
|
482
|
+
with _cuda_device_of(A):
|
|
483
|
+
fn(
|
|
484
|
+
None,
|
|
485
|
+
A.data_ptr(),
|
|
486
|
+
absmax.data_ptr(),
|
|
487
|
+
out.data_ptr(),
|
|
488
|
+
blocksize,
|
|
489
|
+
out.numel(),
|
|
490
|
+
_get_raw_stream(A.device.index),
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@register_kernel("bitsandbytes::gemv_4bit", "cuda")
|
|
495
|
+
def _(
|
|
496
|
+
A: torch.Tensor, B: torch.Tensor, shapeB: Sequence[int], absmax: torch.Tensor, code: torch.Tensor, blocksize: int
|
|
497
|
+
) -> torch.Tensor:
|
|
498
|
+
shape = (*A.shape[:-1], shapeB[0])
|
|
499
|
+
out = torch.empty(shape, device=A.device, dtype=A.dtype)
|
|
500
|
+
_gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize, out=out)
|
|
501
|
+
return out
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
@register_kernel("bitsandbytes::gemv_4bit.out", "cuda")
|
|
505
|
+
def _(
|
|
506
|
+
A: torch.Tensor,
|
|
507
|
+
B: torch.Tensor,
|
|
508
|
+
shapeB: Sequence[int],
|
|
509
|
+
absmax: torch.Tensor,
|
|
510
|
+
code: torch.Tensor,
|
|
511
|
+
blocksize: int,
|
|
512
|
+
out: torch.Tensor,
|
|
513
|
+
) -> None:
|
|
514
|
+
expected_shape = (*A.shape[:-1], shapeB[0])
|
|
515
|
+
if out.shape != expected_shape:
|
|
516
|
+
raise ValueError(f"Expected out.shape == {expected_shape}, got {out.shape}")
|
|
517
|
+
if out.dtype != A.dtype:
|
|
518
|
+
raise ValueError(f"Expected out.dtype == {A.dtype}, got {out.dtype}")
|
|
519
|
+
_gemv_4bit_impl(A, B, shapeB, absmax, code, blocksize, out=out)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _gemv_4bit_impl(
|
|
523
|
+
A: torch.Tensor,
|
|
524
|
+
B: torch.Tensor,
|
|
525
|
+
shapeB: Sequence[int],
|
|
526
|
+
absmax: torch.Tensor,
|
|
527
|
+
code: torch.Tensor,
|
|
528
|
+
blocksize: int,
|
|
529
|
+
out: torch.Tensor,
|
|
530
|
+
) -> None:
|
|
531
|
+
if blocksize not in (32, 64, 128, 256, 512, 1024, 2048, 4096):
|
|
532
|
+
raise ValueError(f"invalid blocksize {blocksize}")
|
|
533
|
+
|
|
534
|
+
# Note: these checks are not strictly necessary, and cost more than they are worth, so they are commented out for now.
|
|
535
|
+
# torch._check(
|
|
536
|
+
# A.numel() == A.size(-1),
|
|
537
|
+
# lambda: f"A must be a vector with leading dimensions of 1, got {A.shape}",
|
|
538
|
+
# )
|
|
539
|
+
# torch._check(
|
|
540
|
+
# A.dtype in [torch.float16, torch.bfloat16, torch.float32],
|
|
541
|
+
# lambda: f"A must be float16, bfloat16, or float32, got {A.dtype}",
|
|
542
|
+
# )
|
|
543
|
+
# torch._check(
|
|
544
|
+
# B.dtype in [torch.uint8, torch.bfloat16, torch.float16, torch.float32],
|
|
545
|
+
# lambda: f"B must be backed by storage of type uint8, bfloat16, float16, or float32, got {B.dtype}",
|
|
546
|
+
# )
|
|
547
|
+
# torch._check(absmax.dtype == torch.float32, lambda: f"absmax must be float32, got {absmax.dtype}")
|
|
548
|
+
# torch._check(code.dtype == torch.float32, lambda: f"code must be float32, got {code.dtype}")
|
|
549
|
+
|
|
550
|
+
m = shapeB[0]
|
|
551
|
+
n = 1
|
|
552
|
+
k = shapeB[1]
|
|
553
|
+
|
|
554
|
+
lda = m
|
|
555
|
+
ldb = (A.shape[-1] + 1) // 2
|
|
556
|
+
ldc = m
|
|
557
|
+
|
|
558
|
+
if A.dtype == torch.float16:
|
|
559
|
+
fn = lib.cgemm_4bit_inference_naive_fp16
|
|
560
|
+
elif A.dtype == torch.bfloat16:
|
|
561
|
+
fn = lib.cgemm_4bit_inference_naive_bf16
|
|
562
|
+
elif A.dtype == torch.float32:
|
|
563
|
+
fn = lib.cgemm_4bit_inference_naive_fp32
|
|
564
|
+
|
|
565
|
+
with _cuda_device_of(A):
|
|
566
|
+
fn(
|
|
567
|
+
m,
|
|
568
|
+
n,
|
|
569
|
+
k,
|
|
570
|
+
A.data_ptr(),
|
|
571
|
+
B.data_ptr(),
|
|
572
|
+
absmax.data_ptr(),
|
|
573
|
+
code.data_ptr(),
|
|
574
|
+
out.data_ptr(),
|
|
575
|
+
lda,
|
|
576
|
+
ldb,
|
|
577
|
+
ldc,
|
|
578
|
+
blocksize,
|
|
579
|
+
_get_raw_stream(A.device.index),
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
@functools.cache
|
|
584
|
+
def _gemm_4bit_use_custom_cuda(device_index, dtype, M, N, K):
|
|
585
|
+
"""Custom kernel vs dequant+F.linear heuristic for M in [5, 1536].
|
|
586
|
+
|
|
587
|
+
Per-arch notes (bf16/fp16, M >= 8, large weight):
|
|
588
|
+
sm75 (T4, ~300 GB/s GDDR6): fp16 MMA only; GDDR makes dequant expensive.
|
|
589
|
+
sm80 (A100, ~2 TB/s HBM2e): mma.sync; HBM thresholds; K-heavy shapes handled explicitly.
|
|
590
|
+
sm86 (A10, ~600 GB/s GDDR6): dedicated block; wider M caps than sm89 at medium N.
|
|
591
|
+
sm89 (4090, L40S, GDDR6X): default fallback; tall-K and large-N get higher M caps.
|
|
592
|
+
sm90 (H100/H200, HBM3/HBM3e): dequant+linear is much faster; thresholds are tight.
|
|
593
|
+
sm100 (B200/B300, HBM3e): exits early at top of function.
|
|
594
|
+
sm120 (RTX 5000, GDDR7): dedicated block; medium-N tiers differ from sm89.
|
|
595
|
+
"""
|
|
596
|
+
if M <= _GEMM_4BIT_CUSTOM_FLOOR_M:
|
|
597
|
+
return True
|
|
598
|
+
|
|
599
|
+
num_sms, major, minor = _gpu_dispatch_props(device_index)
|
|
600
|
+
n_blocks = (N + 63) // 64
|
|
601
|
+
|
|
602
|
+
# fp32 has no MMA kernel; pre-sm75 has no MMA kernel; sm75 has fp16 MMA only.
|
|
603
|
+
# For all of these, custom only wins in the SIMT range (M<8).
|
|
604
|
+
if dtype == torch.float32 or major < 7:
|
|
605
|
+
return M < 8
|
|
606
|
+
if major == 7 and (minor < 5 or dtype != torch.float16):
|
|
607
|
+
return M < 8
|
|
608
|
+
|
|
609
|
+
# sm87 and sm110: no calibration data, conservative fallback.
|
|
610
|
+
if (major == 8 and minor == 7) or major == 11:
|
|
611
|
+
return False
|
|
612
|
+
|
|
613
|
+
# sm100 (B200/B300): dequant+F.linear is significantly faster than our mma.sync kernel.
|
|
614
|
+
if major == 10:
|
|
615
|
+
if n_blocks >= num_sms * 3:
|
|
616
|
+
return M <= 32
|
|
617
|
+
if n_blocks >= num_sms:
|
|
618
|
+
return False if K >= N else M <= 8
|
|
619
|
+
return False
|
|
620
|
+
|
|
621
|
+
is_sm75 = major == 7 and minor == 5
|
|
622
|
+
is_sm80 = major == 8 and minor == 0
|
|
623
|
+
is_sm86 = major == 8 and minor == 6
|
|
624
|
+
is_sm90 = major == 9
|
|
625
|
+
is_sm120 = major == 12 and minor == 0
|
|
626
|
+
is_hbm = is_sm80 or is_sm90 # sm100 already returned above
|
|
627
|
+
tall_k_2xn = K > N * 2
|
|
628
|
+
|
|
629
|
+
# Small-weight path (N*K < 4MB): dequant overhead dominates.
|
|
630
|
+
if N * K < 4 * 1024 * 1024:
|
|
631
|
+
if K * 2 < N:
|
|
632
|
+
# Very short K (K < N/2): latency-dominated, custom 3-9x cheaper.
|
|
633
|
+
if is_hbm:
|
|
634
|
+
# Calibrated on A100: custom wins to M=1536 (low wave), M=512 (high wave).
|
|
635
|
+
# Calibrated on H100/H200: custom wins to M=512 (low wave), M=320 (high wave).
|
|
636
|
+
low_wave = n_blocks * 3 < num_sms
|
|
637
|
+
if is_sm80:
|
|
638
|
+
return M <= (1536 if low_wave else 512)
|
|
639
|
+
return M <= (512 if low_wave else 320)
|
|
640
|
+
if is_sm75:
|
|
641
|
+
# T4: wins require >=3 waves; M cap scales with K depth.
|
|
642
|
+
if n_blocks >= num_sms * 3:
|
|
643
|
+
return M <= 320
|
|
644
|
+
if K >= 1024:
|
|
645
|
+
return M <= 64
|
|
646
|
+
if K >= 704:
|
|
647
|
+
return M <= 96
|
|
648
|
+
return M <= 320
|
|
649
|
+
# sm86/sm89/sm120: well-subscribed wins to M=320; undersubscribed tighter.
|
|
650
|
+
if n_blocks >= num_sms:
|
|
651
|
+
return M <= 320
|
|
652
|
+
return M <= 192 if n_blocks * K > num_sms * 320 else M <= 320
|
|
653
|
+
# K*2 >= N: arch-specific handling at low occupancy.
|
|
654
|
+
quarter_wave = n_blocks * 4 <= num_sms
|
|
655
|
+
if is_sm80 and quarter_wave:
|
|
656
|
+
# A100 <1/4 wave: K>=N loses earlier (K-tiling efficient on HBM2e).
|
|
657
|
+
if K >= N:
|
|
658
|
+
return M <= (32 if n_blocks * 8 <= num_sms else 128)
|
|
659
|
+
return M <= 384
|
|
660
|
+
# T4 <1 wave non-short-K: M>8 routes through occupancy caps below.
|
|
661
|
+
if is_sm75 and n_blocks < num_sms and M > 8:
|
|
662
|
+
return M <= 64
|
|
663
|
+
# General tiers (sm90, sm86, sm89, sm120):
|
|
664
|
+
# GDDR tall-K (K>=N) at <1/4 wave: K-tiling in default impl wins above M=23.
|
|
665
|
+
if quarter_wave:
|
|
666
|
+
return M <= (32 if (K < N or is_hbm) else 23)
|
|
667
|
+
if n_blocks * 2 <= num_sms:
|
|
668
|
+
return M <= 16
|
|
669
|
+
return False # >=1/2 wave: no validated wins for remaining small-weight shapes
|
|
670
|
+
|
|
671
|
+
# Non-small-weight: custom wins up to M=512; dequant+F.linear wins above that.
|
|
672
|
+
if M > 512:
|
|
673
|
+
return False
|
|
674
|
+
|
|
675
|
+
# M=5-7: custom SIMT generally wins because dequant cost dominates.
|
|
676
|
+
# Exceptions where K-tiling efficiency or MMA occupancy favors dequant+F.linear:
|
|
677
|
+
# HBM at M=6-7: tall-K (K>N) at ~3/4 MMA wave.
|
|
678
|
+
# sm90 square (K==N) at specific occupancy bands: arch-specific crossover.
|
|
679
|
+
if M < 8:
|
|
680
|
+
hbm_m67_thresh = 36 if is_sm90 else 48
|
|
681
|
+
if is_hbm and M >= 6 and n_blocks >= hbm_m67_thresh:
|
|
682
|
+
lt_75pct_wave = n_blocks * 4 < num_sms * 3
|
|
683
|
+
lt_60pct_wave = n_blocks * 5 < num_sms * 3
|
|
684
|
+
# Tall-K: K-tiling in default impl wins when under-subscribed.
|
|
685
|
+
if K > N and lt_75pct_wave:
|
|
686
|
+
return False
|
|
687
|
+
# Square: arch-specific crossover around 0.6 wave.
|
|
688
|
+
# A100 (HBM2e): loses below 0.6 wave. H100/H200 (HBM3/3e): loses above.
|
|
689
|
+
if K == N:
|
|
690
|
+
if is_sm80 and lt_60pct_wave:
|
|
691
|
+
return False
|
|
692
|
+
if is_sm90 and lt_75pct_wave and not lt_60pct_wave:
|
|
693
|
+
return False
|
|
694
|
+
return True
|
|
695
|
+
|
|
696
|
+
# M in [8, 512]: per-arch tier ladders.
|
|
697
|
+
|
|
698
|
+
if is_sm75:
|
|
699
|
+
# fp16 MMA (m16n8k8). GDDR bandwidth makes dequant relatively expensive.
|
|
700
|
+
if n_blocks >= num_sms * 3:
|
|
701
|
+
return M <= (128 if K < N else 64)
|
|
702
|
+
if n_blocks >= num_sms // 2:
|
|
703
|
+
return M <= 64
|
|
704
|
+
return M <= 32
|
|
705
|
+
|
|
706
|
+
if is_sm80:
|
|
707
|
+
# mma.sync (m16n8k16). HBM2e thresholds; K-heavy shapes handled explicitly.
|
|
708
|
+
if n_blocks >= num_sms * 3:
|
|
709
|
+
return M <= 128
|
|
710
|
+
if n_blocks >= num_sms:
|
|
711
|
+
return M <= (64 if K < N else 32)
|
|
712
|
+
# Very tall-K (K>=3N) at >1/4 wave: K-tiling in default impl wins at all M.
|
|
713
|
+
# Uses >= to catch K==3N (e.g. N=4096,K=12288 M=9-16: measured regression on A100).
|
|
714
|
+
if K >= N * 3 and n_blocks * 4 > num_sms:
|
|
715
|
+
return False
|
|
716
|
+
# Square (K==N) at 0.5-1 wave: K-tiling wins at ~0.6 wave.
|
|
717
|
+
# n_blocks>=48 excludes small N where SIMT still wins.
|
|
718
|
+
if K == N and n_blocks >= 48 and n_blocks * 5 < num_sms * 3:
|
|
719
|
+
return False
|
|
720
|
+
# <0.5 wave: K<=N custom wins to M=128; K>N default wins above wave threshold.
|
|
721
|
+
if n_blocks * 2 < num_sms:
|
|
722
|
+
if K <= N:
|
|
723
|
+
return M <= 128
|
|
724
|
+
if n_blocks * 3 >= num_sms:
|
|
725
|
+
return False
|
|
726
|
+
# 0.5-1 wave K<N: calibrated on A100 to M=128 (e.g. N=4096,K=1536 M=17-384).
|
|
727
|
+
if n_blocks >= num_sms // 2 and K < N:
|
|
728
|
+
return M <= 128
|
|
729
|
+
return M <= 16
|
|
730
|
+
|
|
731
|
+
if is_sm86:
|
|
732
|
+
# ~600-940 GB/s GDDR6/GDDR6X. Dedicated block: sm89 fallback tiers are too
|
|
733
|
+
# loose for 600 GB/s bandwidth and cause regressions at medium N (~N=4096).
|
|
734
|
+
if n_blocks >= num_sms:
|
|
735
|
+
return M <= 128
|
|
736
|
+
if n_blocks >= num_sms // 2:
|
|
737
|
+
return M <= 64
|
|
738
|
+
return M <= 16
|
|
739
|
+
|
|
740
|
+
if is_sm90:
|
|
741
|
+
# HBM3/HBM3e. dequant+F.linear (WGMMA path) is significantly faster than our
|
|
742
|
+
# mma.sync kernel; thresholds are calibrated conservatively (H100/H200 share path).
|
|
743
|
+
if n_blocks >= num_sms * 3:
|
|
744
|
+
return M <= 64
|
|
745
|
+
if n_blocks >= num_sms * 2:
|
|
746
|
+
return M <= 48
|
|
747
|
+
if n_blocks >= num_sms:
|
|
748
|
+
return M <= 32
|
|
749
|
+
if n_blocks >= num_sms // 2:
|
|
750
|
+
# Square/tall-K at <3/4 wave: K-tiling too efficient on HBM3e.
|
|
751
|
+
if K >= N and n_blocks * 4 < num_sms * 3:
|
|
752
|
+
return False
|
|
753
|
+
return M <= 16
|
|
754
|
+
return False
|
|
755
|
+
|
|
756
|
+
if is_sm120:
|
|
757
|
+
# GDDR7 (~1-1.8 TB/s). Medium-N threshold tiers differ from sm89.
|
|
758
|
+
# sm121 (DGX Spark) has a different bandwidth/SM profile; uses sm89
|
|
759
|
+
# fallback below until validated.
|
|
760
|
+
if n_blocks >= num_sms * 3:
|
|
761
|
+
return M <= 256
|
|
762
|
+
if n_blocks >= num_sms * 2:
|
|
763
|
+
return M <= 128
|
|
764
|
+
# Short-K (K<N) at ~0.8 wave: default impl competitive above M=64.
|
|
765
|
+
if n_blocks * 5 >= num_sms * 4:
|
|
766
|
+
return M <= (96 if K >= N else 64)
|
|
767
|
+
if n_blocks >= num_sms:
|
|
768
|
+
return M <= 64
|
|
769
|
+
if n_blocks >= num_sms // 2:
|
|
770
|
+
# Large-N (n_blocks>=128, N>=8192) with K>=N/2: calibrated on RTX Pro 6000 to M=64.
|
|
771
|
+
return M <= (64 if (K * 2 >= N and n_blocks >= 128) else 8)
|
|
772
|
+
if tall_k_2xn and n_blocks > 64:
|
|
773
|
+
return M <= 16
|
|
774
|
+
return M <= 8
|
|
775
|
+
|
|
776
|
+
# Fallback: sm89 (4090, L40S, L4), sm121 (DGX Spark), unrecognized arches.
|
|
777
|
+
# GDDR bandwidth makes dequant relatively expensive so custom wins at higher M.
|
|
778
|
+
if n_blocks >= num_sms * 3:
|
|
779
|
+
return M <= 256
|
|
780
|
+
if n_blocks >= num_sms * 2:
|
|
781
|
+
return M <= 128
|
|
782
|
+
# Near-wave (~0.8x): tall-K and very large N (n_blocks>=200, N>=14336) raise cap to M=128.
|
|
783
|
+
# N=10240 (n_blocks=160) deliberately excluded to avoid regressions there.
|
|
784
|
+
if n_blocks * 5 >= num_sms * 4:
|
|
785
|
+
if tall_k_2xn or n_blocks >= 200:
|
|
786
|
+
return M <= 128
|
|
787
|
+
# Square/tall-K: >=60 SMs wins to M=128; <60 SMs default wins earlier.
|
|
788
|
+
if K >= N:
|
|
789
|
+
return M <= (128 if num_sms >= 60 else 32)
|
|
790
|
+
return M <= 64
|
|
791
|
+
if n_blocks >= num_sms // 2:
|
|
792
|
+
if tall_k_2xn:
|
|
793
|
+
return M <= 64
|
|
794
|
+
if n_blocks >= 64:
|
|
795
|
+
return M <= 8
|
|
796
|
+
return M <= 32
|
|
797
|
+
# Tall-K (K>N) at narrow N (n_blocks<=48): M-driven crossover.
|
|
798
|
+
# K>=3N (e.g. N=2560,K=10240): SIMT wins to M=12. Moderate K>N: M=10.
|
|
799
|
+
if K > N and n_blocks <= 48:
|
|
800
|
+
return M <= (12 if K >= N * 3 else 10)
|
|
801
|
+
return M <= (16 if (tall_k_2xn or n_blocks < 48) else 8)
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
@functools.cache
|
|
805
|
+
def _gemm_4bit_use_custom_rocm(device_index, dtype, M, N, K):
|
|
806
|
+
"""
|
|
807
|
+
Fused SIMT kernel vs dequant+F.linear heuristic for ROCm.
|
|
808
|
+
|
|
809
|
+
RDNA3/RDNA4 calibration keeps the SIMT kernel through ~M=8.
|
|
810
|
+
CDNA/gfx9 is calibrated on MI308X (gfx942): bf16/fp16 win through M<=4
|
|
811
|
+
after the SIMT math-path tuning, while fp32 only has a broad win through M<=2.
|
|
812
|
+
|
|
813
|
+
TODO: revisit once WMMA/MFMA kernels land.
|
|
814
|
+
"""
|
|
815
|
+
if M <= _GEMM_4BIT_CUSTOM_FLOOR_M and dtype != torch.float32:
|
|
816
|
+
return True
|
|
817
|
+
|
|
818
|
+
arch = _rocm_gfx_arch(device_index)
|
|
819
|
+
if arch.startswith("gfx11") or arch.startswith("gfx12"): # RDNA3 / RDNA4
|
|
820
|
+
return M <= 8
|
|
821
|
+
if arch.startswith("gfx9"): # CDNA / MI-series
|
|
822
|
+
return M <= (2 if dtype == torch.float32 else 4)
|
|
823
|
+
return M <= 4 # unknown ROCm arch: conservative tiny-batch floor
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
@functools.cache
|
|
827
|
+
def _rocm_gfx_arch(device_index):
|
|
828
|
+
"""gfx arch string (e.g. 'gfx1100') for a ROCm device, feature flags stripped."""
|
|
829
|
+
name = getattr(torch.cuda.get_device_properties(device_index), "gcnArchName", "") or ""
|
|
830
|
+
return name.split(":")[0]
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def _gemm_4bit_kernel_impl(
|
|
834
|
+
A, B, shapeB, absmax, blocksize, quant_type, bias=None, absmax_8bit=None, absmax_code=None, absmax_offset=None
|
|
835
|
+
):
|
|
836
|
+
"""Invoke the fused cgemm_4bit_* kernel (shared by the CUDA and ROCm dispatch; the
|
|
837
|
+
C dispatch in gemm_4bit.cu picks SIMT vs MMA per arch/shape). A is made contiguous
|
|
838
|
+
because the kernel reads it as row-major (stride K)."""
|
|
839
|
+
K = A.shape[-1]
|
|
840
|
+
M = A.numel() // K
|
|
841
|
+
N = shapeB[0]
|
|
842
|
+
|
|
843
|
+
if K != shapeB[1]:
|
|
844
|
+
raise RuntimeError(f"A inner dim ({K}) does not match weight ({shapeB[1]})")
|
|
845
|
+
if absmax.dtype != torch.float32:
|
|
846
|
+
raise RuntimeError(f"absmax must be float32, got {absmax.dtype}")
|
|
847
|
+
if bias is not None:
|
|
848
|
+
if bias.ndim != 1:
|
|
849
|
+
raise RuntimeError(f"bias must be 1D, got {bias.ndim}D")
|
|
850
|
+
if bias.dtype != A.dtype:
|
|
851
|
+
raise RuntimeError(f"bias dtype ({bias.dtype}) must match A dtype ({A.dtype})")
|
|
852
|
+
|
|
853
|
+
A = A.contiguous()
|
|
854
|
+
quant_type_int = 1 if quant_type == "fp4" else 2
|
|
855
|
+
out = torch.empty((*A.shape[:-1], N), dtype=A.dtype, device=A.device)
|
|
856
|
+
stream = _get_raw_stream(A.device.index)
|
|
857
|
+
|
|
858
|
+
if A.dtype == torch.bfloat16:
|
|
859
|
+
fn = lib.cgemm_4bit_bf16
|
|
860
|
+
elif A.dtype == torch.float16:
|
|
861
|
+
fn = lib.cgemm_4bit_fp16
|
|
862
|
+
elif A.dtype == torch.float32:
|
|
863
|
+
fn = lib.cgemm_4bit_fp32
|
|
864
|
+
else:
|
|
865
|
+
raise RuntimeError(f"unsupported dtype {A.dtype}")
|
|
866
|
+
|
|
867
|
+
# Offset is expected to be a float32 tensor.
|
|
868
|
+
absmax_offset_f32 = absmax_offset.to(dtype=torch.float32) if absmax_offset is not None else None
|
|
869
|
+
|
|
870
|
+
with _cuda_device_of(A):
|
|
871
|
+
fn(
|
|
872
|
+
A.data_ptr(),
|
|
873
|
+
B.data_ptr(),
|
|
874
|
+
absmax.data_ptr(),
|
|
875
|
+
absmax_8bit.data_ptr() if absmax_8bit is not None else None,
|
|
876
|
+
absmax_code.data_ptr() if absmax_code is not None else None,
|
|
877
|
+
absmax_offset_f32.data_ptr() if absmax_offset_f32 is not None else None,
|
|
878
|
+
out.data_ptr(),
|
|
879
|
+
bias.data_ptr() if bias is not None else None,
|
|
880
|
+
M,
|
|
881
|
+
N,
|
|
882
|
+
K,
|
|
883
|
+
blocksize,
|
|
884
|
+
quant_type_int,
|
|
885
|
+
stream,
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
return out
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
def _dequant_linear_fallback(
|
|
892
|
+
A, B, shapeB, absmax, blocksize, quant_type, bias=None, absmax_8bit=None, absmax_code=None, absmax_offset=None
|
|
893
|
+
):
|
|
894
|
+
"""Unfused fallback shared by CUDA and ROCm: reconstruct the (optionally nested)
|
|
895
|
+
absmax, dequantize the 4-bit weight via the backend dequant impls (reusing
|
|
896
|
+
preallocated buffers), then F.linear."""
|
|
897
|
+
if absmax_8bit is not None:
|
|
898
|
+
absmax_dq = torch.empty_like(absmax_8bit, dtype=torch.float32)
|
|
899
|
+
_dequantize_blockwise_impl(absmax_8bit, absmax, absmax_code, 256, torch.float32, out=absmax_dq)
|
|
900
|
+
absmax = absmax_dq + absmax_offset
|
|
901
|
+
B_dq = torch.empty(shapeB, dtype=A.dtype, device=A.device)
|
|
902
|
+
_dequantize_4bit_impl(B, absmax, blocksize, quant_type, A.dtype, out=B_dq)
|
|
903
|
+
return torch.nn.functional.linear(A, B_dq, bias)
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
# Unified CUDA/ROCm dispatch for bitsandbytes::gemm_4bit. The choice *among* custom
|
|
907
|
+
# kernels (CUDA SIMT vs MMA; ROCm SIMT) is made in the C dispatch (csrc/gemm_4bit.cu).
|
|
908
|
+
_GEMM_4BIT_CUSTOM_FLOOR_M = 4
|
|
909
|
+
if torch.version.hip is None:
|
|
910
|
+
_gemm_4bit_use_custom_fn = _gemm_4bit_use_custom_cuda
|
|
911
|
+
# CUDA: dequant+F.linear wins past M=1536 (dequant savings negligible at very
|
|
912
|
+
# large batch).
|
|
913
|
+
_gemm_4bit_custom_max_m = 1536
|
|
914
|
+
else:
|
|
915
|
+
_gemm_4bit_use_custom_fn = _gemm_4bit_use_custom_rocm
|
|
916
|
+
# ROCm: the custom path is SIMT-only today; the per-arch heuristic above owns
|
|
917
|
+
# RDNA/CDNA thresholds. Keep a hard upper cap while WMMA/MFMA paths are absent.
|
|
918
|
+
_gemm_4bit_custom_max_m = 256
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
@register_kernel("bitsandbytes::gemm_4bit", "cuda")
|
|
922
|
+
def _(
|
|
923
|
+
A: torch.Tensor,
|
|
924
|
+
B: torch.Tensor,
|
|
925
|
+
shapeB: Sequence[int],
|
|
926
|
+
absmax: torch.Tensor,
|
|
927
|
+
blocksize: int,
|
|
928
|
+
quant_type: str,
|
|
929
|
+
bias: Optional[torch.Tensor] = None,
|
|
930
|
+
absmax_8bit: Optional[torch.Tensor] = None,
|
|
931
|
+
absmax_code: Optional[torch.Tensor] = None,
|
|
932
|
+
absmax_offset: Optional[torch.Tensor] = None,
|
|
933
|
+
) -> torch.Tensor:
|
|
934
|
+
K = A.shape[-1]
|
|
935
|
+
M = A.numel() // K
|
|
936
|
+
N = shapeB[0]
|
|
937
|
+
|
|
938
|
+
# The backend-specific heuristic owns tiny-M floors and per-arch thresholds.
|
|
939
|
+
# Past custom_max_m (or for blocksize-misaligned K), use the dequant+F.linear
|
|
940
|
+
# fallback.
|
|
941
|
+
if M > _gemm_4bit_custom_max_m:
|
|
942
|
+
use_custom = False
|
|
943
|
+
elif K % blocksize != 0:
|
|
944
|
+
warn(
|
|
945
|
+
f"inner dimension ({K}) is not aligned for fast kernel "
|
|
946
|
+
f"with blocksize={blocksize}, falling back to slower implementation.",
|
|
947
|
+
UserWarning,
|
|
948
|
+
)
|
|
949
|
+
use_custom = False
|
|
950
|
+
else:
|
|
951
|
+
use_custom = _gemm_4bit_use_custom_fn(A.device.index, A.dtype, M, N, K)
|
|
952
|
+
|
|
953
|
+
if not use_custom:
|
|
954
|
+
return _dequant_linear_fallback(
|
|
955
|
+
A,
|
|
956
|
+
B,
|
|
957
|
+
shapeB,
|
|
958
|
+
absmax,
|
|
959
|
+
blocksize,
|
|
960
|
+
quant_type,
|
|
961
|
+
bias,
|
|
962
|
+
absmax_8bit=absmax_8bit,
|
|
963
|
+
absmax_code=absmax_code,
|
|
964
|
+
absmax_offset=absmax_offset,
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
return _gemm_4bit_kernel_impl(
|
|
968
|
+
A, B, shapeB, absmax, blocksize, quant_type, bias, absmax_8bit, absmax_code, absmax_offset
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
"""C FUNCTIONS FOR OPTIMIZERS"""
|
|
973
|
+
str2optimizer32bit = {
|
|
974
|
+
"adam": (
|
|
975
|
+
lib.cadam32bit_grad_fp32,
|
|
976
|
+
lib.cadam32bit_grad_fp16,
|
|
977
|
+
lib.cadam32bit_grad_bf16,
|
|
978
|
+
),
|
|
979
|
+
"momentum": (
|
|
980
|
+
lib.cmomentum32bit_grad_32,
|
|
981
|
+
lib.cmomentum32bit_grad_16,
|
|
982
|
+
),
|
|
983
|
+
"rmsprop": (
|
|
984
|
+
lib.crmsprop32bit_grad_32,
|
|
985
|
+
lib.crmsprop32bit_grad_16,
|
|
986
|
+
),
|
|
987
|
+
"lion": (
|
|
988
|
+
lib.clion32bit_grad_fp32,
|
|
989
|
+
lib.clion32bit_grad_fp16,
|
|
990
|
+
lib.clion32bit_grad_bf16,
|
|
991
|
+
),
|
|
992
|
+
"adagrad": (
|
|
993
|
+
lib.cadagrad32bit_grad_32,
|
|
994
|
+
lib.cadagrad32bit_grad_16,
|
|
995
|
+
),
|
|
996
|
+
"lamb": (
|
|
997
|
+
lib.cadam32bit_grad_fp32,
|
|
998
|
+
lib.cadam32bit_grad_fp16,
|
|
999
|
+
lib.cadam32bit_grad_bf16,
|
|
1000
|
+
),
|
|
1001
|
+
"ademamix": (
|
|
1002
|
+
lib.cademamix32bit_grad_fp32,
|
|
1003
|
+
lib.cademamix32bit_grad_fp16,
|
|
1004
|
+
lib.cademamix32bit_grad_bf16,
|
|
1005
|
+
),
|
|
1006
|
+
"lars": (
|
|
1007
|
+
lib.cmomentum32bit_grad_32,
|
|
1008
|
+
lib.cmomentum32bit_grad_16,
|
|
1009
|
+
),
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
str2optimizer8bit_blockwise = {
|
|
1013
|
+
"adam": (
|
|
1014
|
+
lib.cadam_8bit_blockwise_grad_fp32,
|
|
1015
|
+
lib.cadam_8bit_blockwise_grad_fp16,
|
|
1016
|
+
lib.cadam_8bit_blockwise_grad_bf16,
|
|
1017
|
+
),
|
|
1018
|
+
"momentum": (
|
|
1019
|
+
lib.cmomentum_8bit_blockwise_grad_fp32,
|
|
1020
|
+
lib.cmomentum_8bit_blockwise_grad_fp16,
|
|
1021
|
+
lib.cmomentum_8bit_blockwise_grad_bf16,
|
|
1022
|
+
),
|
|
1023
|
+
"rmsprop": (
|
|
1024
|
+
lib.crmsprop_8bit_blockwise_grad_fp32,
|
|
1025
|
+
lib.crmsprop_8bit_blockwise_grad_fp16,
|
|
1026
|
+
lib.crmsprop_8bit_blockwise_grad_bf16,
|
|
1027
|
+
),
|
|
1028
|
+
"lion": (
|
|
1029
|
+
lib.clion_8bit_blockwise_grad_fp32,
|
|
1030
|
+
lib.clion_8bit_blockwise_grad_fp16,
|
|
1031
|
+
lib.clion_8bit_blockwise_grad_bf16,
|
|
1032
|
+
),
|
|
1033
|
+
"adagrad": (
|
|
1034
|
+
lib.cadagrad_8bit_blockwise_grad_fp32,
|
|
1035
|
+
lib.cadagrad_8bit_blockwise_grad_fp16,
|
|
1036
|
+
lib.cadagrad_8bit_blockwise_grad_bf16,
|
|
1037
|
+
),
|
|
1038
|
+
"ademamix": (
|
|
1039
|
+
lib.cademamix_8bit_blockwise_grad_fp32,
|
|
1040
|
+
lib.cademamix_8bit_blockwise_grad_fp16,
|
|
1041
|
+
lib.cademamix_8bit_blockwise_grad_bf16,
|
|
1042
|
+
),
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
def _optimizer_update_32bit_impl(
|
|
1047
|
+
optimizer_name: str,
|
|
1048
|
+
g: torch.Tensor,
|
|
1049
|
+
p: torch.Tensor,
|
|
1050
|
+
state1: torch.Tensor,
|
|
1051
|
+
state2: Optional[torch.Tensor],
|
|
1052
|
+
unorm_vec: Optional[torch.Tensor],
|
|
1053
|
+
max_unorm: float,
|
|
1054
|
+
param_norm: float,
|
|
1055
|
+
beta1: float,
|
|
1056
|
+
beta2: float,
|
|
1057
|
+
beta3: float,
|
|
1058
|
+
alpha: float,
|
|
1059
|
+
eps: float,
|
|
1060
|
+
weight_decay: float,
|
|
1061
|
+
step: int,
|
|
1062
|
+
lr: float,
|
|
1063
|
+
gnorm_scale: float,
|
|
1064
|
+
skip_zeros=False,
|
|
1065
|
+
) -> None:
|
|
1066
|
+
optim_fns = str2optimizer32bit.get(optimizer_name, None)
|
|
1067
|
+
if optim_fns is None:
|
|
1068
|
+
raise ValueError(
|
|
1069
|
+
f"Unsupported optimizer name: {optimizer_name}. Supported optimizers: {list(str2optimizer32bit.keys())}"
|
|
1070
|
+
)
|
|
1071
|
+
if g.dtype == torch.float32:
|
|
1072
|
+
optim_func = optim_fns[0]
|
|
1073
|
+
elif g.dtype == torch.float16:
|
|
1074
|
+
optim_func = optim_fns[1]
|
|
1075
|
+
elif g.dtype == torch.bfloat16 and len(optim_fns) == 3:
|
|
1076
|
+
optim_func = optim_fns[2]
|
|
1077
|
+
else:
|
|
1078
|
+
raise ValueError(
|
|
1079
|
+
f"Gradient+optimizer bit data type combination not supported: grad {g.dtype}, optimizer {state1.dtype}",
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
with _cuda_device_of(g):
|
|
1083
|
+
optim_func(
|
|
1084
|
+
get_ptr(g),
|
|
1085
|
+
get_ptr(p),
|
|
1086
|
+
get_ptr(state1),
|
|
1087
|
+
get_ptr(state2),
|
|
1088
|
+
get_ptr(unorm_vec),
|
|
1089
|
+
ct.c_float(max_unorm),
|
|
1090
|
+
ct.c_float(param_norm),
|
|
1091
|
+
ct.c_float(beta1),
|
|
1092
|
+
ct.c_float(beta2),
|
|
1093
|
+
ct.c_float(beta3),
|
|
1094
|
+
ct.c_float(alpha),
|
|
1095
|
+
ct.c_float(eps),
|
|
1096
|
+
ct.c_float(weight_decay),
|
|
1097
|
+
ct.c_int32(step),
|
|
1098
|
+
ct.c_float(lr),
|
|
1099
|
+
ct.c_float(gnorm_scale),
|
|
1100
|
+
ct.c_bool(skip_zeros),
|
|
1101
|
+
ct.c_int32(g.numel()),
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
def _optimizer_update_8bit_blockwise_impl(
|
|
1106
|
+
optimizer_name: str,
|
|
1107
|
+
g: torch.Tensor,
|
|
1108
|
+
p: torch.Tensor,
|
|
1109
|
+
state1: torch.Tensor,
|
|
1110
|
+
state2: Optional[torch.Tensor],
|
|
1111
|
+
beta1: float,
|
|
1112
|
+
beta2: float,
|
|
1113
|
+
beta3: float,
|
|
1114
|
+
alpha: float,
|
|
1115
|
+
eps: float,
|
|
1116
|
+
step: int,
|
|
1117
|
+
lr: float,
|
|
1118
|
+
qmap1: torch.Tensor,
|
|
1119
|
+
qmap2: Optional[torch.Tensor],
|
|
1120
|
+
absmax1: torch.Tensor,
|
|
1121
|
+
absmax2: Optional[torch.Tensor],
|
|
1122
|
+
weight_decay: float,
|
|
1123
|
+
gnorm_scale: float,
|
|
1124
|
+
skip_zeros=False,
|
|
1125
|
+
) -> None:
|
|
1126
|
+
# torch._check(
|
|
1127
|
+
# g.numel() == p.numel(),
|
|
1128
|
+
# lambda: f"g and p must have the same number of elements, got {g.numel()} and {p.numel()}",
|
|
1129
|
+
# )
|
|
1130
|
+
# compute_dtypes = [torch.float16, torch.bfloat16, torch.float32]
|
|
1131
|
+
|
|
1132
|
+
# torch._check(
|
|
1133
|
+
# g.dtype in compute_dtypes,
|
|
1134
|
+
# lambda: f"g must be bfloat16, float16, or float32, got {g.dtype}",
|
|
1135
|
+
# )
|
|
1136
|
+
# torch._check(
|
|
1137
|
+
# g.dtype == p.dtype,
|
|
1138
|
+
# lambda: f"Expected all tensors to have the same dtype, got g.dtype={g.dtype}, p.dtype={p.dtype}",
|
|
1139
|
+
# )
|
|
1140
|
+
# torch._check(
|
|
1141
|
+
# state1.dtype == torch.uint8,
|
|
1142
|
+
# lambda: f"state1 must be uint8, got {state1.dtype}",
|
|
1143
|
+
# )
|
|
1144
|
+
# torch._check(
|
|
1145
|
+
# qmap1.dtype == absmax1.dtype == torch.float32,
|
|
1146
|
+
# lambda: f"Expected qmap1 and absmax1 to be float32, got qmap1.dtype={qmap1.dtype}, absmax1.dtype={absmax1.dtype}",
|
|
1147
|
+
# )
|
|
1148
|
+
# if state2 is not None:
|
|
1149
|
+
# torch._check(
|
|
1150
|
+
# state2.dtype == torch.uint8,
|
|
1151
|
+
# lambda: f"state2 must be uint8, got {state2.dtype}",
|
|
1152
|
+
# )
|
|
1153
|
+
# torch._check(
|
|
1154
|
+
# qmap2.dtype == absmax2.dtype == torch.float32,
|
|
1155
|
+
# lambda: f"Expected qmap2 and absmax2 to be float32, got qmap2.dtype={qmap2.dtype}, absmax2.dtype={absmax2.dtype}",
|
|
1156
|
+
# )
|
|
1157
|
+
optimizer_fns = str2optimizer8bit_blockwise.get(optimizer_name)
|
|
1158
|
+
if optimizer_fns is None:
|
|
1159
|
+
raise ValueError(
|
|
1160
|
+
f"Unsupported optimizer name: {optimizer_name}. Supported optimizers: {list(str2optimizer8bit_blockwise.keys())}"
|
|
1161
|
+
)
|
|
1162
|
+
|
|
1163
|
+
if g.dtype == torch.float32:
|
|
1164
|
+
optimizer_fn = optimizer_fns[0]
|
|
1165
|
+
elif g.dtype == torch.float16:
|
|
1166
|
+
optimizer_fn = optimizer_fns[1]
|
|
1167
|
+
elif g.dtype == torch.bfloat16:
|
|
1168
|
+
optimizer_fn = optimizer_fns[2]
|
|
1169
|
+
else:
|
|
1170
|
+
raise ValueError(
|
|
1171
|
+
f"Unsupported gradient dtype: {g.dtype}. Supported dtypes: torch.float32, torch.float16, torch.bfloat16"
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
with _cuda_device_of(g):
|
|
1175
|
+
optimizer_fn(
|
|
1176
|
+
get_ptr(p),
|
|
1177
|
+
get_ptr(g),
|
|
1178
|
+
get_ptr(state1),
|
|
1179
|
+
get_ptr(state2),
|
|
1180
|
+
ct.c_float(beta1),
|
|
1181
|
+
ct.c_float(beta2),
|
|
1182
|
+
ct.c_float(beta3),
|
|
1183
|
+
ct.c_float(alpha),
|
|
1184
|
+
ct.c_float(eps),
|
|
1185
|
+
ct.c_int32(step),
|
|
1186
|
+
ct.c_float(lr),
|
|
1187
|
+
get_ptr(qmap1),
|
|
1188
|
+
get_ptr(qmap2),
|
|
1189
|
+
get_ptr(absmax1),
|
|
1190
|
+
get_ptr(absmax2),
|
|
1191
|
+
ct.c_float(weight_decay),
|
|
1192
|
+
ct.c_float(gnorm_scale),
|
|
1193
|
+
ct.c_bool(skip_zeros),
|
|
1194
|
+
ct.c_int32(g.numel()),
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
register_kernel("bitsandbytes::optimizer_update_8bit_blockwise", "cuda")(_optimizer_update_8bit_blockwise_impl)
|
|
1199
|
+
register_kernel("bitsandbytes::optimizer_update_32bit", "cuda")(_optimizer_update_32bit_impl)
|