ZhengyangZhang commited on
Commit
16a2d8c
·
verified ·
1 Parent(s): 6e8e00b

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/deepspeed/compression/__init__.py +8 -0
  2. lib/python3.12/site-packages/deepspeed/compression/__pycache__/__init__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/deepspeed/compression/__pycache__/basic_layer.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/deepspeed/compression/__pycache__/compress.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/deepspeed/compression/__pycache__/config.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/deepspeed/compression/__pycache__/constants.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/deepspeed/compression/__pycache__/helper.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/deepspeed/compression/__pycache__/scheduler.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/deepspeed/compression/__pycache__/utils.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/deepspeed/compression/basic_layer.py +840 -0
  11. lib/python3.12/site-packages/deepspeed/compression/compress.py +239 -0
  12. lib/python3.12/site-packages/deepspeed/compression/config.py +452 -0
  13. lib/python3.12/site-packages/deepspeed/compression/constants.py +188 -0
  14. lib/python3.12/site-packages/deepspeed/compression/helper.py +322 -0
  15. lib/python3.12/site-packages/deepspeed/compression/scheduler.py +173 -0
  16. lib/python3.12/site-packages/deepspeed/compression/utils.py +222 -0
  17. lib/python3.12/site-packages/deepspeed/ops/__pycache__/__init__.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/deepspeed/ops/compile/__init__.py +6 -0
  19. lib/python3.12/site-packages/deepspeed/ops/compile/__pycache__/__init__.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/deepspeed/ops/csrc/adagrad/cpu_adagrad.cpp +215 -0
  21. lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam.cpp +13 -0
  22. lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam_impl.cpp +244 -0
  23. lib/python3.12/site-packages/deepspeed/ops/csrc/adam/fused_adam_frontend.cpp +25 -0
  24. lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_adam.cu +203 -0
  25. lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_apply.cuh +132 -0
  26. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.cpp +341 -0
  27. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.h +38 -0
  28. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.cpp +76 -0
  29. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.h +59 -0
  30. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.cpp +132 -0
  31. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.h +81 -0
  32. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.cpp +40 -0
  33. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.h +43 -0
  34. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.cpp +51 -0
  35. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.h +38 -0
  36. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.cpp +109 -0
  37. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.h +38 -0
  38. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.cpp +61 -0
  39. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.h +30 -0
  40. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.cpp +123 -0
  41. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.h +28 -0
  42. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.cpp +28 -0
  43. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.h +22 -0
  44. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.cpp +135 -0
  45. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.h +43 -0
  46. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.cpp +322 -0
  47. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.h +94 -0
  48. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/py_ds_aio.cpp +116 -0
  49. lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_test/single_process_config.json +29 -0
  50. lib/python3.12/site-packages/deepspeed/ops/csrc/compile/deepcompile.cpp +188 -0
lib/python3.12/site-packages/deepspeed/compression/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ from .compress import init_compression, redundancy_clean
7
+ from .scheduler import compression_scheduler
8
+ from .helper import convert_conv1d_to_linear
lib/python3.12/site-packages/deepspeed/compression/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (393 Bytes). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/basic_layer.cpython-312.pyc ADDED
Binary file (45.6 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/compress.cpython-312.pyc ADDED
Binary file (9.85 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/config.cpython-312.pyc ADDED
Binary file (22.3 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/constants.cpython-312.pyc ADDED
Binary file (5.5 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/helper.cpython-312.pyc ADDED
Binary file (17.5 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/scheduler.cpython-312.pyc ADDED
Binary file (8.81 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/__pycache__/utils.cpython-312.pyc ADDED
Binary file (9.63 kB). View file
 
lib/python3.12/site-packages/deepspeed/compression/basic_layer.py ADDED
@@ -0,0 +1,840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ import torch
7
+ import math
8
+ from torch import nn
9
+ from torch.nn import init
10
+ import deepspeed.comm as dist
11
+ from .utils import TopKBinarizer, SymQuantizer, AsymQuantizer, TernaryQuantizer, BinaryQuantizer
12
+ from deepspeed.utils import logger
13
+
14
+ g_mpu = None
15
+
16
+
17
+ class QuantAct(nn.Module):
18
+ """
19
+ Class to quantize given activations. Note that when using this function, the input activation quantization range will be fixed for all
20
+ tokens/images for inference. This generally will affect some accuracy but achieve better latency performance.
21
+ Parameters:
22
+ ----------
23
+ act_range_momentum : float, default 0.95
24
+ Momentum for updating the activation quantization range.
25
+ quant_mode : str, default 'symmetric'
26
+ """
27
+
28
+ def __init__(self, act_range_momentum=0.95, quant_mode='symmetric'):
29
+ super(QuantAct, self).__init__()
30
+
31
+ self.act_range_momentum = act_range_momentum
32
+ self.quant_mode = quant_mode
33
+ if quant_mode == 'symmetric':
34
+ self.act_function = SymQuantizer.apply
35
+ else:
36
+ self.act_function = AsymQuantizer.apply
37
+
38
+ self.register_buffer('x_min_max', torch.zeros(2))
39
+
40
+ def forward(self, x, num_bits, *args):
41
+ """
42
+ x: the activation that we need to quantize
43
+ num_bits: the number of bits we need to quantize the activation to
44
+ *args: some extra arguments that are useless but needed for align with the interface of other quantization functions
45
+ """
46
+
47
+ if self.training:
48
+ x_min = x.data.min()
49
+ x_max = x.data.max()
50
+
51
+ # Initialization
52
+ if self.x_min_max[0] == self.x_min_max[1]:
53
+ self.x_min_max[0] = x_min
54
+ self.x_min_max[1] = x_max
55
+
56
+ # if do not need momentum, please set self.act_range_momentum = 0
57
+ self.x_min_max[0] = self.x_min_max[0] * self.act_range_momentum + x_min * (1 - self.act_range_momentum)
58
+ self.x_min_max[1] = self.x_min_max[1] * self.act_range_momentum + x_max * (1 - self.act_range_momentum)
59
+
60
+ x_q = self.act_function(x, num_bits, self.x_min_max[0], self.x_min_max[1])
61
+
62
+ return x_q
63
+
64
+
65
+ class Embedding_Compress(nn.Embedding):
66
+
67
+ def __init__(self, *kargs):
68
+ super(Embedding_Compress, self).__init__(*kargs)
69
+ self.weight.start_bits = None
70
+ self.weight.target_bits = None
71
+ self.weight.q_period = None
72
+ self.weight_quantization_enabled_in_forward = False
73
+ self.weight_quantization_enabled = False
74
+
75
+ def extra_repr(self):
76
+ return 'num_embeddings={}, embedding_dim={}, weight_quantization={}'.format(
77
+ self.num_embeddings, self.embedding_dim, self.weight.target_bits)
78
+
79
+ def enable_weight_quantization(self, start_bits, target_bits, quantization_period,
80
+ weight_quantization_enabled_in_forward, quantization_type, num_groups):
81
+ self.weight.start_bits = start_bits
82
+ self.weight.target_bits = target_bits
83
+ self.weight.q_period = quantization_period
84
+ self.weight_quantization_enabled_in_forward = weight_quantization_enabled_in_forward
85
+ if self.weight_quantization_enabled_in_forward:
86
+ logger.warning(
87
+ "************ A lot of MoQ features are not supported in quantize_weight_in_forward mode, please consider to use DS-FP16 optimizer************"
88
+ )
89
+ if self.weight.target_bits >= 3:
90
+ if quantization_type == 'symmetric':
91
+ self.weight_quantizer = SymQuantizer.apply
92
+ else:
93
+ self.weight_quantizer = AsymQuantizer.apply
94
+ elif self.weight.target_bits == 2:
95
+ assert quantization_type == 'symmetric', 'Only symmetric quantization is supported for ternary weight quantization'
96
+ self.weight_quantizer = TernaryQuantizer.apply
97
+ elif self.weight.target_bits == 1:
98
+ assert quantization_type == 'symmetric', 'Only symmetric quantization is supported for binary weight quantization'
99
+ self.weight_quantizer = BinaryQuantizer.apply
100
+ # for embedding, we always use token-wise quantization
101
+ self.weight_quantize_num_groups = self.weight.size(0)
102
+
103
+ def fix_weight_quantization(self):
104
+ self.weight.data = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
105
+ self.weight_quantize_num_groups).data
106
+ self.weight_quantization_enabled_in_forward = False
107
+ return None
108
+
109
+ def forward(self, input):
110
+ if self.weight_quantization_enabled_in_forward and self.weight_quantization_enabled:
111
+ weight = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
112
+ self.weight_quantize_num_groups)
113
+ else:
114
+ weight = self.weight
115
+
116
+ out = nn.functional.embedding(input, weight, self.padding_idx, self.max_norm, self.norm_type,
117
+ self.scale_grad_by_freq, self.sparse)
118
+ return out
119
+
120
+
121
+ class LinearLayer_Compress(nn.Linear):
122
+ """
123
+ Linear layer with compression.
124
+ """
125
+
126
+ def __init__(self, *kargs, bias=True):
127
+ super(LinearLayer_Compress, self).__init__(*kargs, bias=bias)
128
+ self.sparse_pruning_method = None
129
+ self.row_pruning_method = None
130
+ self.head_pruning_method = None
131
+ self.activation_quantization_method = None
132
+ self.weight.start_bits = None
133
+ self.weight.target_bits = None
134
+ self.weight.q_period = None
135
+ self.weight_quantization_enabled_in_forward = False
136
+ self.weight_quantization_enabled = False
137
+ self.sparse_pruning_enabled = False
138
+ self.row_pruning_enabled = False
139
+ self.head_pruning_enabled = False
140
+ self.activation_quantization_enabled = False
141
+
142
+ def extra_repr(self):
143
+ return 'in_features={}, out_features={}, bias={}, sparse pruning={}, row pruning={}, head pruning={}, activation quantization={}, weight_quantization={}'.format(
144
+ self.in_features, self.out_features, self.bias is not None, self.sparse_pruning_method is not None, \
145
+ self.row_pruning_method is not None, self.head_pruning_method is not None, self.activation_quantization_method is not None, self.weight.target_bits)
146
+
147
+ def enable_sparse_pruning(self, ratio, method):
148
+ # Here, we support two cases: L1 norm based pruning and topk based pruning
149
+ self.sparse_pruning_ratio = ratio
150
+ self.sparse_pruning_method = method
151
+ if method == 'l1':
152
+ weight_norm = torch.abs(self.weight.data)
153
+ mask = TopKBinarizer.apply(weight_norm, self.sparse_pruning_ratio, False)
154
+ mask = mask.view(self.weight.size())
155
+ mask = mask.to(self.weight.device)
156
+ elif method == 'topk':
157
+ self.sparse_mask_scores = nn.Parameter(torch.Tensor(self.weight.size()))
158
+ self.sparse_mask_scores.data = self.sparse_mask_scores.data.to(self.weight.device)
159
+ init.kaiming_uniform_(self.sparse_mask_scores, a=math.sqrt(5))
160
+ mask = None
161
+ else:
162
+ raise NotImplementedError
163
+
164
+ self.register_buffer('sparse_pruning_mask', mask)
165
+
166
+ def enable_row_pruning(self, ratio, method):
167
+ # Here, we support two cases: L1 norm based pruning and topk based pruning
168
+ self.row_pruning_ratio = ratio
169
+ self.row_pruning_method = method
170
+
171
+ if method == 'l1':
172
+ # compute the l1 norm of each column
173
+ weight_norm = torch.linalg.norm(self.weight.data, ord=1, dim=1)
174
+ mask = TopKBinarizer.apply(weight_norm, self.row_pruning_ratio, False)
175
+ mask = mask.view(-1, 1)
176
+ mask = mask.to(self.weight.device)
177
+ elif method == 'topk':
178
+ self.row_mask_scores = nn.Parameter(torch.Tensor(self.weight.size(0), 1))
179
+ self.row_mask_scores.data = self.row_mask_scores.data.to(self.weight.device)
180
+ init.kaiming_uniform_(self.row_mask_scores, a=math.sqrt(5))
181
+ mask = None
182
+ else:
183
+ raise NotImplementedError
184
+
185
+ self.register_buffer('row_pruning_mask', mask)
186
+
187
+ def enable_head_pruning(self, ratio, method, num_heads):
188
+ # Here, we support only topk based pruning
189
+ self.num_heads = num_heads
190
+ self.head_pruning_ratio = ratio
191
+ self.head_pruning_method = method
192
+
193
+ if method not in ['topk']:
194
+ raise NotImplementedError
195
+ else:
196
+ self.head_pruning_ratio = ratio
197
+ self.head_pruning_scores = nn.Parameter(torch.Tensor(1,
198
+ self.num_heads)) # we apply the pruning to O matrix
199
+ self.head_pruning_scores.data = self.head_pruning_scores.data.to(self.weight.device)
200
+ init.kaiming_uniform_(self.head_pruning_scores, a=math.sqrt(5))
201
+
202
+ def fix_sparse_pruning_helper(self):
203
+ mask = self.get_mask(pruning_type='sparse')
204
+ self.weight.data = self.weight.data * mask
205
+ del self.sparse_pruning_mask
206
+ if self.sparse_pruning_method == 'topk':
207
+ del self.sparse_mask_scores
208
+ self.sparse_pruning_method = None
209
+ self.sparse_pruning_enabled = False
210
+ return None
211
+
212
+ def fix_row_col_pruning_helper(self, mask=None, dim_reduction=False):
213
+ # This function is used for row/col pruning
214
+ # particularly, if we have two back-to-back layers, F1 and F2; when
215
+ # we remove rows from F1, we also need to remove columns from F2
216
+ # However, if we only have one layer, F1, then we only need to mask pruned
217
+ # rows as 0 in F1
218
+ if mask is None:
219
+ mask = self.get_mask(pruning_type='row').bool()
220
+ if dim_reduction:
221
+ start_bits = self.weight.start_bits
222
+ target_bits = self.weight.target_bits
223
+ q_period = self.weight.q_period
224
+ self.weight = nn.Parameter(self.weight.data[mask.view(-1), :])
225
+ self.weight.start_bits = start_bits
226
+ self.weight.target_bits = target_bits
227
+ self.weight.q_period = q_period
228
+ if self.bias is not None:
229
+ self.bias = nn.Parameter(self.bias.data[mask.view(-1)])
230
+ self.out_features = self.weight.size(0)
231
+ else:
232
+ self.weight.data = self.weight.data * mask.view(-1, 1)
233
+ if self.bias is not None:
234
+ self.bias.data = self.bias.data * mask.view(-1)
235
+
236
+ del self.row_pruning_mask
237
+ if self.row_pruning_method == 'topk':
238
+ del self.row_mask_scores
239
+ self.row_pruning_method = None
240
+ else:
241
+ # this is generally for column pruning
242
+ start_bits = self.weight.start_bits
243
+ target_bits = self.weight.target_bits
244
+ q_period = self.weight.q_period
245
+ self.weight = nn.Parameter(self.weight.data[:, mask.view(-1)])
246
+ self.weight.start_bits = start_bits
247
+ self.weight.target_bits = target_bits
248
+ self.weight.q_period = q_period
249
+ self.in_features = self.weight.size(1)
250
+ mask = None
251
+ self.row_pruning_enabled = False
252
+ return mask
253
+
254
+ def fix_head_pruning_helper(self, mask=None, num_heads=None, dim_reduction=False):
255
+ # similar as row/col pruning, head pruning also needs to prune QKV which is associated with O matrix
256
+ num_heads = num_heads if num_heads else self.num_heads
257
+ if mask is None:
258
+ if self.head_pruning_method == 'topk':
259
+ mask = self.get_mask(pruning_type='head').bool()
260
+ if dim_reduction:
261
+ shape = self.weight.size(0)
262
+ start_bits = self.weight.start_bits
263
+ target_bits = self.weight.target_bits
264
+ q_period = self.weight.q_period
265
+ self.weight = nn.Parameter(self.weight.data.t().reshape(num_heads,
266
+ -1)[mask.view(-1), :].reshape(-1,
267
+ shape).t())
268
+ self.weight.start_bits = start_bits
269
+ self.weight.target_bits = target_bits
270
+ self.weight.q_period = q_period
271
+ else:
272
+
273
+ shape = self.weight.size()
274
+ self.weight.data = (self.weight.data.t().reshape(self.num_heads, -1) * mask.view(-1, 1)).reshape(
275
+ shape[1], shape[0]).t()
276
+
277
+ if self.head_pruning_method == 'topk':
278
+ del self.head_pruning_scores
279
+ self.head_pruning_method = None
280
+ else:
281
+ raise NotImplementedError
282
+ else:
283
+ start_bits = self.weight.start_bits
284
+ target_bits = self.weight.target_bits
285
+ q_period = self.weight.q_period
286
+ shape = self.weight.size(1)
287
+ self.weight = nn.Parameter(self.weight.data.reshape(num_heads, -1)[mask.view(-1), :].reshape(-1, shape))
288
+ self.weight.start_bits = start_bits
289
+ self.weight.target_bits = target_bits
290
+ self.weight.q_period = q_period
291
+ if self.bias is not None:
292
+ self.bias = nn.Parameter(self.bias.data.reshape(num_heads, -1)[mask.view(-1), :].reshape(-1))
293
+ self.head_pruning_enabled = False
294
+ return mask
295
+
296
+ def get_mask(self, pruning_type='row'):
297
+ if pruning_type == 'sparse':
298
+ if self.sparse_pruning_method == 'l1':
299
+ return self.sparse_pruning_mask.to(self.weight.device)
300
+ elif self.sparse_pruning_method == 'topk':
301
+ return TopKBinarizer.apply(self.sparse_mask_scores, self.sparse_pruning_ratio, False)
302
+ else:
303
+ raise NotImplementedError
304
+ if pruning_type == 'row':
305
+ if self.row_pruning_method == 'l1':
306
+ return self.row_pruning_mask.to(self.weight.device)
307
+ elif self.row_pruning_method == 'topk':
308
+ return TopKBinarizer.apply(self.row_mask_scores, self.row_pruning_ratio, False)
309
+ else:
310
+ raise NotImplementedError
311
+ elif pruning_type == 'head':
312
+ if self.head_pruning_method == 'topk':
313
+ return TopKBinarizer.apply(self.head_pruning_scores, self.head_pruning_ratio, False)
314
+ else:
315
+ raise NotImplementedError
316
+ else:
317
+ raise NotImplementedError
318
+
319
+ def enable_weight_quantization(self, start_bits, target_bits, quantization_period,
320
+ weight_quantization_enabled_in_forward, quantization_type, num_groups):
321
+ self.weight.start_bits = start_bits
322
+ self.weight.target_bits = target_bits
323
+ self.weight.q_period = quantization_period
324
+ self.weight_quantization_enabled_in_forward = weight_quantization_enabled_in_forward
325
+ if self.weight_quantization_enabled_in_forward:
326
+ logger.warning(
327
+ "************ A lot of MoQ features are not supported in quantize_weight_in_forward mode, please consider to use DS-FP16 optimizer************"
328
+ )
329
+ if self.weight.target_bits >= 3:
330
+ if quantization_type == 'symmetric':
331
+ self.weight_quantizer = SymQuantizer.apply
332
+ else:
333
+ self.weight_quantizer = AsymQuantizer.apply
334
+ elif self.weight.target_bits == 2:
335
+ assert quantization_type == 'symmetric', 'Only symmetric quantization is supported for ternary weight quantization'
336
+ self.weight_quantizer = TernaryQuantizer.apply
337
+ elif self.weight.target_bits == 1:
338
+ assert quantization_type == 'symmetric', 'Only symmetric quantization is supported for binary weight quantization'
339
+ self.weight_quantizer = BinaryQuantizer.apply
340
+ self.weight_quantize_num_groups = num_groups
341
+
342
+ def fix_weight_quantization(self):
343
+ self.weight.data = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
344
+ self.weight_quantize_num_groups).data
345
+ self.weight_quantization_enabled_in_forward = False
346
+ return None
347
+
348
+ def enable_activation_quantization(self, bits, quantization_type, range_calibration):
349
+ assert bits in [4, 8], 'Only 4/8 bits activation quantization are supported for now'
350
+ self.activation_quantization_bits = bits
351
+ self.activation_quantization_method = f"{quantization_type}_{range_calibration}"
352
+ if range_calibration == 'static':
353
+ self.activation_quantizer = QuantAct(quant_mode=quantization_type)
354
+ else:
355
+ if quantization_type == 'symmetric':
356
+ self.activation_quantizer = SymQuantizer.apply
357
+ else:
358
+ self.activation_quantizer = AsymQuantizer.apply
359
+
360
+ def head_pruning_reshape(self, w, mask):
361
+ shape = w.shape
362
+ return (w.t().reshape(self.num_heads, -1) * mask.view(-1, 1)).reshape(shape[1], shape[0]).t()
363
+
364
+ def forward(self, input, skip_bias_add=False):
365
+
366
+ if self.weight_quantization_enabled_in_forward and self.weight_quantization_enabled:
367
+ weight = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
368
+ self.weight_quantize_num_groups)
369
+ bias = self.bias
370
+ else:
371
+ weight = self.weight
372
+ bias = self.bias
373
+
374
+ if self.sparse_pruning_enabled and self.sparse_pruning_method:
375
+ mask = self.get_mask(pruning_type='sparse')
376
+ weight = weight * mask.view(self.weight.size())
377
+
378
+ if self.row_pruning_enabled and self.row_pruning_method:
379
+ mask = self.get_mask(pruning_type='row')
380
+ weight = weight * mask.view(-1, 1)
381
+ if bias is not None:
382
+ bias = bias * mask.view(-1)
383
+
384
+ if self.head_pruning_enabled and self.head_pruning_method:
385
+ mask = self.get_mask(pruning_type='head')
386
+ weight = self.head_pruning_reshape(weight, mask)
387
+
388
+ if self.activation_quantization_enabled:
389
+ if 'dynamic' in self.activation_quantization_method:
390
+ num_groups = input.numel() // input.size(-1)
391
+ else:
392
+ num_groups = 1
393
+ input = self.activation_quantizer(input, self.activation_quantization_bits, None, None, num_groups)
394
+
395
+ if skip_bias_add:
396
+ # used for mpu linear layers
397
+ output = nn.functional.linear(input, weight, None)
398
+ return output, bias
399
+ else:
400
+ output = nn.functional.linear(input, weight, bias)
401
+ return output
402
+
403
+
404
+ class Conv2dLayer_Compress(nn.Conv2d):
405
+ """
406
+ Conv2D layer with compression.
407
+ """
408
+
409
+ def __init__(self, *kargs):
410
+ super(Conv2dLayer_Compress, self).__init__(*kargs)
411
+ self.sparse_pruning_method = None
412
+ self.channel_pruning_method = None
413
+ self.activation_quantization_method = None
414
+ self.weight.start_bits = None
415
+ self.weight.target_bits = None
416
+ self.weight.q_period = None
417
+ self.weight_quantization_enabled_in_forward = False
418
+ self.sparse_pruning_enabled = False
419
+ self.channel_pruning_enabled = False
420
+ self.activation_quantization_enabled = False
421
+
422
+ def __repr__(self):
423
+ s = ('{in_channels}, {out_channels}, kernel_size={kernel_size}'
424
+ ', stride={stride}')
425
+ if self.padding != (0, ) * len(self.padding):
426
+ s += ', padding={padding}'
427
+ if self.dilation != (1, ) * len(self.dilation):
428
+ s += ', dilation={dilation}'
429
+ if self.output_padding != (0, ) * len(self.output_padding):
430
+ s += ', output_padding={output_padding}'
431
+ if self.groups != 1:
432
+ s += ', groups={groups}'
433
+ if self.bias is None:
434
+ s += ', bias=False'
435
+ if self.padding_mode != 'zeros':
436
+ s += ', padding_mode={padding_mode}'
437
+ output = s.format(**self.__dict__)
438
+
439
+ return output + ' sparse pruning={}, channel pruning={}, activation quantization={}, weight_quantization={}'.format(
440
+ self.sparse_pruning_method is not None, self.channel_pruning_method is not None,
441
+ self.activation_quantization_method is not None, self.weight.target_bits)
442
+
443
+ def enable_sparse_pruning(self, ratio, method):
444
+ self.sparse_pruning_ratio = ratio
445
+ self.sparse_pruning_method = method
446
+ if method == 'l1':
447
+ weight_norm = torch.abs(self.weight.data)
448
+ mask = TopKBinarizer.apply(weight_norm, self.sparse_pruning_ratio, False)
449
+ mask = mask.view(self.weight.size())
450
+ mask = mask.to(self.weight.device)
451
+ elif method == 'topk':
452
+ self.sparse_mask_scores = nn.Parameter(torch.Tensor(self.weight.size()))
453
+ self.sparse_mask_scores.data = self.sparse_mask_scores.data.to(self.weight.device)
454
+ init.kaiming_uniform_(self.sparse_mask_scores, a=math.sqrt(5))
455
+ mask = None
456
+ else:
457
+ raise NotImplementedError
458
+
459
+ self.register_buffer('sparse_pruning_mask', mask)
460
+
461
+ def enable_channel_pruning(self, ratio, method):
462
+ # Here, we support two cases: L1 norm based pruning and topk based pruning
463
+ self.channel_pruning_ratio = ratio
464
+ self.channel_pruning_method = method
465
+
466
+ if method == 'l1':
467
+ # compute the l1 norm of each conv2d kernel (the last three dimension)
468
+ weight_norm = torch.linalg.norm(self.weight.data, ord=1, dim=[1, 2, 3])
469
+ mask = TopKBinarizer.apply(weight_norm, self.channel_pruning_ratio, False)
470
+ mask = mask.view(-1, 1, 1, 1)
471
+ mask = mask.to(self.weight.device)
472
+ elif method == 'topk':
473
+ self.channel_mask_scores = nn.Parameter(torch.Tensor(self.weight.size(0), 1, 1, 1))
474
+ self.channel_mask_scores.data = self.channel_mask_scores.data.to(self.weight.device)
475
+ init.kaiming_uniform_(self.channel_mask_scores, a=math.sqrt(5))
476
+ mask = None
477
+ else:
478
+ raise NotImplementedError
479
+
480
+ self.register_buffer('channel_pruning_mask', mask)
481
+
482
+ def fix_sparse_pruning_helper(self):
483
+ mask = self.get_mask(pruning_type='sparse')
484
+ self.weight.data = self.weight.data * mask
485
+ del self.sparse_pruning_mask
486
+ if self.sparse_pruning_method == 'topk':
487
+ del self.sparse_mask_scores
488
+ self.sparse_pruning_method = None
489
+ self.sparse_pruning_enabled = False
490
+ return None
491
+
492
+ def fix_channel_pruning_helper(self, mask=None, dim_reduction=False):
493
+ if mask is None:
494
+ if self.channel_pruning_method in ['l1', 'topk']:
495
+ mask = self.get_mask(pruning_type='channel').bool()
496
+ if dim_reduction:
497
+ start_bits = self.weight.start_bits
498
+ target_bits = self.weight.target_bits
499
+ q_period = self.weight.q_period
500
+ self.weight = nn.Parameter(self.weight.data[mask.view(-1), ...])
501
+ self.weight.start_bits = start_bits
502
+ self.weight.target_bits = target_bits
503
+ self.weight.q_period = q_period
504
+ if self.bias is not None:
505
+ self.bias = nn.Parameter(self.bias.data[mask.view(-1)])
506
+ else:
507
+ self.weight.data = self.weight.data * mask.view(-1, 1, 1, 1)
508
+ if self.bias is not None:
509
+ self.bias.data = self.bias.data * mask.view(-1)
510
+ del self.channel_pruning_mask
511
+ if self.channel_pruning_method == 'topk':
512
+ del self.channel_mask_scores
513
+ self.channel_pruning_method = None
514
+ else:
515
+ raise NotImplementedError
516
+ else:
517
+ start_bits = self.weight.start_bits
518
+ target_bits = self.weight.target_bits
519
+ q_period = self.weight.q_period
520
+ self.weight = nn.Parameter(self.weight.data[:, mask.view(-1), ...])
521
+ self.weight.start_bits = start_bits
522
+ self.weight.target_bits = target_bits
523
+ self.weight.q_period = q_period
524
+ mask = None
525
+ self.channel_pruning_enabled = False
526
+ return mask
527
+
528
+ def get_mask(self, pruning_type='sparse'):
529
+ if pruning_type == 'sparse':
530
+ if self.sparse_pruning_method == 'l1':
531
+ return self.sparse_pruning_mask.to(self.weight.device)
532
+ elif self.sparse_pruning_method == 'topk':
533
+ return TopKBinarizer.apply(self.sparse_mask_scores, self.sparse_pruning_ratio, False)
534
+ else:
535
+ raise NotImplementedError
536
+ elif pruning_type == 'channel':
537
+ if self.channel_pruning_method == 'l1':
538
+ return self.channel_pruning_mask.to(self.weight.device)
539
+ elif self.channel_pruning_method == 'topk':
540
+ return TopKBinarizer.apply(self.channel_mask_scores, self.channel_pruning_ratio, False)
541
+ else:
542
+ raise NotImplementedError
543
+ else:
544
+ raise NotImplementedError
545
+
546
+ def fix_weight_quantization(self):
547
+ self.weight.data = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
548
+ self.weight_quantize_num_groups).data
549
+ self.weight_quantization_enabled_in_forward = False
550
+ return None
551
+
552
+ def enable_weight_quantization(self, start_bits, target_bits, quantization_period,
553
+ weight_quantization_enabled_in_forward, quantization_type, num_groups):
554
+ self.weight.start_bits = start_bits
555
+ self.weight.target_bits = target_bits
556
+ self.weight.q_period = quantization_period
557
+ self.weight_quantization_enabled_in_forward = weight_quantization_enabled_in_forward
558
+ if self.weight_quantization_enabled_in_forward:
559
+ assert self.weight.target_bits >= 4, 'Only >=4 bits weight quantization are supported during forward pass for now'
560
+ logger.warning(
561
+ "************ A lot of MoQ features are not supported in quantize_weight_in_forward mode, please consider to use DS-FP16 optimizer************"
562
+ )
563
+ if quantization_type == 'symmetric':
564
+ self.weight_quantizer = SymQuantizer.apply
565
+ else:
566
+ self.weight_quantizer = AsymQuantizer.apply
567
+ self.weight_quantize_num_groups = num_groups
568
+
569
+ def enable_activation_quantization(self, bits, quantization_type, range_calibration):
570
+ assert bits in [4, 8], 'Only 4/8 bits activation quantization are supported for now'
571
+ self.activation_quantization_bits = bits
572
+ self.activation_quantization_method = f"{quantization_type}_{range_calibration}"
573
+ if range_calibration == 'static':
574
+ self.activation_quantizer = QuantAct(quant_mode=quantization_type)
575
+ else:
576
+ if quantization_type == 'symmetric':
577
+ self.activation_quantizer = SymQuantizer.apply
578
+ else:
579
+ self.activation_quantizer = AsymQuantizer.apply
580
+
581
+ def forward(self, input):
582
+
583
+ if self.weight_quantization_enabled_in_forward and self.weight_quantization_enabled:
584
+ weight = self.weight_quantizer(self.weight, self.weight.target_bits, None, None,
585
+ self.weight_quantize_num_groups)
586
+ bias = self.bias
587
+ else:
588
+ weight = self.weight
589
+ bias = self.bias
590
+
591
+ if self.sparse_pruning_enabled and self.sparse_pruning_method:
592
+ mask = self.get_mask(pruning_type='sparse')
593
+ weight = weight * mask.view(self.weight.size())
594
+
595
+ if self.channel_pruning_enabled:
596
+ mask = self.get_mask(pruning_type='channel')
597
+ weight = weight * mask.view(-1, 1, 1, 1)
598
+ if bias is not None:
599
+ bias = bias * mask.view(-1)
600
+
601
+ if self.activation_quantization_enabled:
602
+ if 'dynamic' in self.activation_quantization_method:
603
+ num_groups = input.numel() // input[0].numel()
604
+ else:
605
+ num_groups = 1
606
+ input = self.activation_quantizer(input, self.activation_quantization_bits, None, None, num_groups)
607
+
608
+ return nn.functional.conv2d(input, weight, bias, self.stride, self.padding, self.dilation, self.groups)
609
+
610
+
611
+ class BNLayer_Compress(nn.BatchNorm2d):
612
+
613
+ def fix_channel_pruning_helper(self, mask, dim_reduction=True):
614
+ self.weight = nn.Parameter(self.weight.data[mask.view(-1)])
615
+ self.bias = nn.Parameter(self.bias.data[mask.view(-1)])
616
+ self.running_mean = self.running_mean[mask.view(-1)]
617
+ self.running_var = self.running_var[mask.view(-1)]
618
+
619
+
620
+ def _reduce(input_):
621
+ """All-reduce the input tensor across model parallel group."""
622
+ group = g_mpu.get_model_parallel_group()
623
+
624
+ # Bypass the function if we are using only 1 GPU.
625
+ if dist.get_world_size(group=group) == 1:
626
+ return input_
627
+
628
+ # All-reduce.
629
+ dist.all_reduce(input_, group=group)
630
+
631
+ return input_
632
+
633
+
634
+ def split_tensor_along_last_dim(tensor, num_partitions, contiguous_split_chunks=False):
635
+ """Split a tensor along its last dimension.
636
+ Arguments:
637
+ tensor: input tensor.
638
+ num_partitions: number of partitions to split the tensor
639
+ contiguous_split_chunks: If True, make each chunk contiguous
640
+ in memory.
641
+ """
642
+ # Get the size and dimension.
643
+ last_dim = tensor.dim() - 1
644
+ assert tensor.size()[last_dim] % num_partitions == 0
645
+ last_dim_size = tensor.size()[last_dim] // num_partitions
646
+ # Split.
647
+ tensor_list = torch.split(tensor, last_dim_size, dim=last_dim)
648
+ # Note: torch.split does not create contiguous tensors by default.
649
+ if contiguous_split_chunks:
650
+ return tuple(chunk.contiguous() for chunk in tensor_list)
651
+
652
+ return tensor_list
653
+
654
+
655
+ def _split(input_):
656
+ """Split the tensor along its last dimension and keep the
657
+ corresponding slice."""
658
+ group = g_mpu.get_model_parallel_group()
659
+
660
+ # Bypass the function if we are using only 1 GPU.
661
+ if dist.get_world_size(group=group) == 1:
662
+ return input_
663
+
664
+ # Split along last dimension.
665
+ world_size = dist.get_world_size(group=group)
666
+ input_list = split_tensor_along_last_dim(input_, world_size)
667
+
668
+ # Note: torch.split does not create contiguous tensors by default.
669
+ rank = dist.get_rank(group=group)
670
+ output = input_list[rank].contiguous()
671
+
672
+ return output
673
+
674
+
675
+ def _gather(input_):
676
+ """Gather tensors and concatenate along the last dimension."""
677
+ group = g_mpu.get_model_parallel_group()
678
+
679
+ # Bypass the function if we are using only 1 GPU.
680
+ if dist.get_world_size(group=group) == 1:
681
+ return input_
682
+
683
+ # Size and dimension.
684
+ last_dim = input_.dim() - 1
685
+ rank = dist.get_rank(group=group)
686
+ world_size = dist.get_world_size(group=group)
687
+
688
+ tensor_list = [torch.empty_like(input_) for _ in range(world_size)]
689
+ tensor_list[rank] = input_
690
+ dist.all_gather(tensor_list, input_, group=group)
691
+
692
+ # Note: torch.cat already creates a contiguous tensor.
693
+ output = torch.cat(tensor_list, dim=last_dim).contiguous()
694
+
695
+ return output
696
+
697
+
698
+ class _CopyToModelParallelRegion(torch.autograd.Function):
699
+ """Pass the input to the model parallel region."""
700
+
701
+ @staticmethod
702
+ def forward(ctx, input_):
703
+ return input_
704
+
705
+ @staticmethod
706
+ def backward(ctx, grad_output):
707
+ return _reduce(grad_output)
708
+
709
+
710
+ class _ReduceFromModelParallelRegion(torch.autograd.Function):
711
+ """All-reduce the input from the model parallel region."""
712
+
713
+ @staticmethod
714
+ def forward(ctx, input_):
715
+ return _reduce(input_)
716
+
717
+ @staticmethod
718
+ def backward(ctx, grad_output):
719
+ return grad_output
720
+
721
+
722
+ class _ScatterToModelParallelRegion(torch.autograd.Function):
723
+ """Split the input and keep only the corresponding chuck to the rank."""
724
+
725
+ @staticmethod
726
+ def forward(ctx, input_):
727
+ return _split(input_)
728
+
729
+ @staticmethod
730
+ def backward(ctx, grad_output):
731
+ return _gather(grad_output)
732
+
733
+
734
+ class _GatherFromModelParallelRegion(torch.autograd.Function):
735
+ """Gather the input from model parallel region and concatenate."""
736
+
737
+ @staticmethod
738
+ def forward(ctx, input_):
739
+ return _gather(input_)
740
+
741
+ @staticmethod
742
+ def backward(ctx, grad_output):
743
+ return _split(grad_output)
744
+
745
+
746
+ # -----------------
747
+ # Helper functions.
748
+ # -----------------
749
+
750
+
751
+ def copy_to_model_parallel_region(input_):
752
+ return _CopyToModelParallelRegion.apply(input_)
753
+
754
+
755
+ def reduce_from_model_parallel_region(input_):
756
+ return _ReduceFromModelParallelRegion.apply(input_)
757
+
758
+
759
+ def scatter_to_model_parallel_region(input_):
760
+ return _ScatterToModelParallelRegion.apply(input_)
761
+
762
+
763
+ def gather_from_model_parallel_region(input_):
764
+ return _GatherFromModelParallelRegion.apply(input_)
765
+
766
+
767
+ class ColumnParallelLinear_Compress(LinearLayer_Compress):
768
+
769
+ def __init__(self, mpu, input_size, output_size, bias=True, gather_output=True, skip_bias_add=False):
770
+ # Keep input parameters
771
+ global g_mpu
772
+ g_mpu = mpu
773
+ self.input_size = input_size
774
+ self.output_size = output_size
775
+ self.gather_output = gather_output
776
+ self.skip_bias_add = skip_bias_add
777
+
778
+ # Divide the weight matrix along the last dimension.
779
+ world_size = mpu.get_model_parallel_world_size()
780
+ assert output_size % world_size == 0
781
+ self.output_size_per_partition = output_size // world_size
782
+
783
+ super(ColumnParallelLinear_Compress, self).__init__(self.input_size, self.output_size_per_partition, bias=bias)
784
+
785
+ def forward(self, input_):
786
+ # Set up backprop all-reduce.
787
+ input_parallel = copy_to_model_parallel_region(input_)
788
+ # Matrix multiply.
789
+ if self.skip_bias_add:
790
+ output_parallel, bias = super().forward(input_parallel, True)
791
+ else:
792
+ output_parallel = super().forward(input_parallel)
793
+ bias = None
794
+ if self.gather_output:
795
+ # All-gather across the partitions.
796
+ output = gather_from_model_parallel_region(output_parallel)
797
+ else:
798
+ output = output_parallel
799
+ return output, bias
800
+
801
+
802
+ class RowParallelLinear_Compress(LinearLayer_Compress):
803
+
804
+ def __init__(self, mpu, input_size, output_size, bias=True, input_is_parallel=False, skip_bias_add=False):
805
+ # Keep input parameters
806
+ global g_mpu
807
+ g_mpu = mpu
808
+ self.input_size = input_size
809
+ self.output_size = output_size
810
+ self.input_is_parallel = input_is_parallel
811
+ self.skip_bias_add = skip_bias_add
812
+
813
+ # Divide the weight matrix along the last dimension.
814
+ world_size = mpu.get_model_parallel_world_size()
815
+ assert input_size % world_size == 0
816
+ self.input_size_per_partition = input_size // world_size
817
+
818
+ super(RowParallelLinear_Compress, self).__init__(self.input_size_per_partition, self.output_size, bias=bias)
819
+
820
+ def forward(self, input_):
821
+ # Set up backprop all-reduce.
822
+ if self.input_is_parallel:
823
+ input_parallel = input_
824
+ else:
825
+ input_parallel = scatter_to_model_parallel_region(input_)
826
+ # Matrix multiply.
827
+ output_parallel, bias = super().forward(input_parallel, True)
828
+
829
+ # All-reduce across all the partitions.
830
+ output_ = reduce_from_model_parallel_region(output_parallel)
831
+ if not self.skip_bias_add:
832
+ if bias is not None:
833
+ output = output_ + bias
834
+ else:
835
+ output = output_
836
+ output_bias = None
837
+ else:
838
+ output = output_
839
+ output_bias = bias
840
+ return output, output_bias
lib/python3.12/site-packages/deepspeed/compression/compress.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ import re
7
+ from .helper import compression_preparation, fix_compression, recursive_getattr, is_module_compressible
8
+ from .config import get_compression_config
9
+ from ..runtime.config_utils import dict_raise_error_on_duplicate_keys
10
+ from .constants import *
11
+ import os
12
+ import json
13
+
14
+ try:
15
+ import neural_compressor as nc
16
+ except ImportError as e:
17
+ nc = None
18
+
19
+
20
+ def check_deepspeed_config(config):
21
+ if isinstance(config, dict):
22
+ return config
23
+ elif os.path.exists(config):
24
+ return json.load(open(config, "r"), object_pairs_hook=dict_raise_error_on_duplicate_keys)
25
+ else:
26
+ raise ValueError(
27
+ f"Expected a string path to an existing deepspeed config, or a dictionary. Received: {config}")
28
+
29
+
30
+ def get_module_name(group_name, model, key_word, exist_module_name, mpu=None, verbose=True):
31
+ '''
32
+ get the associated module name from the model based on the key_word provided by users
33
+ '''
34
+ return_module_name = []
35
+ for name, module in model.named_modules():
36
+
37
+ module_check = is_module_compressible(module, mpu)
38
+
39
+ if re.search(key_word, name) is not None and module_check:
40
+ if name in exist_module_name and verbose:
41
+ # logger.warning
42
+ raise ValueError(
43
+ f"{name} is already added to compression, please check your config file for {group_name}.")
44
+ if name not in exist_module_name:
45
+ exist_module_name.add(name)
46
+ return_module_name.append(name)
47
+ return return_module_name, exist_module_name
48
+
49
+
50
+ def get_compress_methods(model, compress_methods, mpu=None):
51
+ # extract the compression module for each method in compress_methods
52
+ layer_added_compress_methods = []
53
+ for method, method_content in compress_methods.items():
54
+ if LAYER_REDUCTION in method:
55
+ continue
56
+ # for loop different methods, i.e., weight quantization, activation quantization etc
57
+ exist_module_name = set()
58
+ shared_parameters = method_content[SHARED_PARAMETERS] # get all the shared parameters
59
+ for group_name, method_parameters in method_content[DIFFERENT_GROUPS].items():
60
+ # for loop different groups, i.e., weight quantization group 1, weight quantization group 2 etc
61
+ module_name_list = []
62
+ related_module_name_list = []
63
+ if method_parameters[DIFFERENT_GROUPS_RELATED_MODULE_SCOPE]:
64
+ # this is used for head/row/channel pruning, if users provide the related module scope, we can shrink the layer dim for them
65
+ # otherwise we just mask those as zeros
66
+ for key_word, related_key_words in zip(method_parameters[DIFFERENT_GROUPS_MODULE_SCOPE],
67
+ method_parameters[DIFFERENT_GROUPS_RELATED_MODULE_SCOPE]):
68
+ module_name, exist_module_name = get_module_name(group_name,
69
+ model,
70
+ key_word,
71
+ exist_module_name,
72
+ mpu=mpu)
73
+ module_name_list.append(module_name)
74
+ tmp_related_module_name_list = []
75
+ for rkw in related_key_words:
76
+ # related key word can be a list, for instance the QKV for O matrix in Attention
77
+ module_name, _ = get_module_name(group_name, model, rkw, set(), mpu=mpu)
78
+ tmp_related_module_name_list.append(module_name)
79
+ related_module_name_list.append(tmp_related_module_name_list)
80
+ else:
81
+ for key_word in method_parameters[DIFFERENT_GROUPS_MODULE_SCOPE]:
82
+ module_name, exist_module_name = get_module_name(group_name,
83
+ model,
84
+ key_word,
85
+ exist_module_name,
86
+ mpu=mpu)
87
+ module_name_list.append(module_name)
88
+
89
+ if module_name_list:
90
+ # combine shared parameters with each group
91
+ combined_method_parameters = {
92
+ **(method_parameters.copy().pop(DIFFERENT_GROUPS_PARAMETERS)),
93
+ **shared_parameters
94
+ }
95
+ compression_item = [module_name_list, related_module_name_list, {method: combined_method_parameters}]
96
+ layer_added_compress_methods.append(compression_item)
97
+ return layer_added_compress_methods
98
+
99
+
100
+ def init_compression(model, deepspeed_config, teacher_model=None, mpu=None):
101
+ """
102
+ Compress a model: replace linear/conv2d layer with deepspeed compression-aware modules
103
+ Args:
104
+ model (`torch.nn.Module`)
105
+ The model to compress.
106
+ deepspeed_config (`DeepSpeedConfig`)
107
+ The path of ds_config
108
+ mpu
109
+ The mpu module for Row/Column parallelism
110
+ """
111
+ compress_methods = get_compression_config(check_deepspeed_config(deepspeed_config))
112
+ if hasattr(model, 'module'):
113
+ c_model = model.module
114
+ else:
115
+ c_model = model
116
+
117
+ # For layer reduction
118
+ if compress_methods[LAYER_REDUCTION][LAYER_REDUCTION_ENABLED]:
119
+ assert teacher_model is not None, "Teacher model is required for layer reduction"
120
+ student_initialization(c_model, teacher_model, deepspeed_config)
121
+
122
+ layer_added_compress_methods = get_compress_methods(c_model, compress_methods, mpu=mpu)
123
+ compression_preparation(c_model, layer_added_compress_methods, mpu)
124
+
125
+ # For sparse pruning snip_momentum method
126
+ shared_parameters = compress_methods[SPARSE_PRUNING][SHARED_PARAMETERS]
127
+ if shared_parameters[SPARSE_PRUNING_ENABLED] and \
128
+ shared_parameters[SPARSE_PRUNING_METHOD] == SPARSE_PRUNING_METHOD_SNIP_MOMENTUM:
129
+
130
+ assert nc is not None, "please ensure the neural_compressor python package is installed by pip or conda if user wants to use snip_momentum sparse pruning"
131
+
132
+ from .helper import generate_pruners, register_on_step_begin
133
+ from nc import WeightPruningConfig
134
+
135
+ config = WeightPruningConfig(target_sparsity=1 - shared_parameters[SPARSE_PRUNING_DENSE_RATIO],
136
+ pattern=shared_parameters[SPARSE_PRUNING_BLOCK_PATTERN],
137
+ pruning_frequency=shared_parameters[SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE],
138
+ start_step=shared_parameters[SPARSE_PRUNING_SCHEDULE_OFFSET],
139
+ end_step=shared_parameters[SPARSE_PRUNING_SCHEDULE_OFFSET_END],
140
+ excluded_op_names=shared_parameters[SPARSE_PRUNING_EXCLUDED_MODULES])
141
+ pruners = generate_pruners(config, c_model)
142
+ c_model.pruners = pruners
143
+ register_on_step_begin(c_model)
144
+
145
+ return model
146
+
147
+
148
+ def redundancy_clean(model, deepspeed_config, mpu=None):
149
+ """
150
+ Remove the redundancy of a model
151
+ Args:
152
+ model (`torch.nn.Module`)
153
+ The model to compress.
154
+ deepspeed_config (`DeepSpeedConfig`)
155
+ The path of ds_config
156
+ mpu
157
+ The mpu module for Row/Column parallelism
158
+ """
159
+ compress_methods = get_compression_config(check_deepspeed_config(deepspeed_config))
160
+ if hasattr(model, 'module'):
161
+ c_model = model.module
162
+ else:
163
+ c_model = model
164
+
165
+ layer_added_compress_methods_tmp = get_compress_methods(c_model, compress_methods, mpu=mpu)
166
+ # sort methods
167
+ order_list = [
168
+ WEIGHT_QUANTIZATION, SPARSE_PRUNING, ROW_PRUNING, HEAD_PRUNING, CHANNEL_PRUNING, ACTIVATION_QUANTIZATION
169
+ ]
170
+ layer_added_compress_methods = sorted(layer_added_compress_methods_tmp,
171
+ key=lambda x: order_list.index(list(x[2].keys())[0]))
172
+
173
+ for module_name_lists, related_module_name_lists, compression_technique in layer_added_compress_methods:
174
+ stored_mask = []
175
+ need_mask = True if related_module_name_lists else False
176
+ for i, mnl in enumerate(module_name_lists):
177
+ for module_name in mnl:
178
+ mask = fix_compression(c_model, module_name, compression_technique, dim_reduction=need_mask)
179
+ if need_mask:
180
+ stored_mask.append(mask)
181
+ if need_mask:
182
+ for rmnl in related_module_name_lists[i]:
183
+ for j, module_name in enumerate(rmnl):
184
+ mask = fix_compression(c_model,
185
+ module_name,
186
+ compression_technique,
187
+ mask=stored_mask[j],
188
+ dim_reduction=True)
189
+ return model
190
+
191
+
192
+ def student_initialization(student_model, teacher_model, deepspeed_config):
193
+ '''
194
+ Given a student model and a teacher model, select the
195
+ Args:
196
+ student_model (`torch.nn.Module`)
197
+ The model we will update weight
198
+ teacher_model (`torch.nn.Module`)
199
+ The model guide the student to learn
200
+ deepspeed_config (`DeepSpeedConfig`)
201
+ The path of ds_config
202
+ '''
203
+ config = get_compression_config(check_deepspeed_config(deepspeed_config))
204
+ compress_methods = config[LAYER_REDUCTION]
205
+
206
+ module_name_prefix = compress_methods[MODULE_NAME_PREFIX]
207
+ teacher_layer = compress_methods[TEACHER_LAYER]
208
+ student_layer = [i for i in range(len(teacher_layer))]
209
+ other_module_name = compress_methods[OTHER_MODULE_NAME]
210
+ '''
211
+ name_prefix (`str`)
212
+ The prefix name before the layer #.
213
+ Example 1: bert.encoder.layer, for BERT_base model's prefix name
214
+ Example 2: transformer.h, for GPT-2 hugging face prefix name
215
+ teacher_layer (`list of integers`)
216
+ The layer of teacher will be used for student's reinitialization
217
+ Example 1: [1,3,5,7,9], means we want to matches the 2nd/4th/6th/8th/10th layer of teacher to the first 5 layers of student
218
+ student_layer (`list` or None)
219
+ The layer of student need to be re-initialized
220
+ Example 1: None, means we want to reinitialize all the layers
221
+ Example 1: [0,1,2,3,4], means we want to reinitialize the first 5 layers
222
+ other_module_name (`list of string`)
223
+ The modules will be used for student's reinitialization
224
+ Example 1: ['bert.pooler', 'bert.embeddings', 'classifier'], means we want to apply the weight in teacher's embedding/pooler/classier module to the student
225
+ Example 2: ['transformer.w', 'transformer.ln_f', 'lm_head'], means we want to apply the weight in teacher's embedding layers module to the student
226
+ Note that teacher_layer should matches student layer
227
+ '''
228
+ assert len(student_layer) == len(teacher_layer)
229
+ for s_name, t_name in zip(student_layer, teacher_layer):
230
+ s_module = recursive_getattr(student_model, module_name_prefix + '.' + str(s_name))
231
+ t_module = recursive_getattr(teacher_model, module_name_prefix + '.' + str(t_name))
232
+ for s_param, t_param in zip(s_module.parameters(), t_module.parameters()):
233
+ s_param.data.copy_(t_param.data)
234
+ for name in other_module_name:
235
+ s_module = recursive_getattr(student_model, name)
236
+ t_module = recursive_getattr(teacher_model, name)
237
+ print(name)
238
+ for s_param, t_param in zip(s_module.parameters(), t_module.parameters()):
239
+ s_param.data.copy_(t_param.data)
lib/python3.12/site-packages/deepspeed/compression/config.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ from .constants import *
7
+ import copy
8
+ from ..runtime.config_utils import get_scalar_param, get_list_param
9
+
10
+
11
+ def get_compression_config(param_dict):
12
+ #
13
+ output = {}
14
+
15
+ if COMPRESSION_TRAINING not in param_dict.keys():
16
+ param_dict[COMPRESSION_TRAINING] = {}
17
+ sub_param_dict = param_dict[COMPRESSION_TRAINING]
18
+ output[WEIGHT_QUANTIZATION] = get_weight_quantization(sub_param_dict)
19
+ output[ACTIVATION_QUANTIZATION] = get_activation_quantization(sub_param_dict)
20
+ output[SPARSE_PRUNING] = get_sparse_pruning(sub_param_dict)
21
+ output[ROW_PRUNING] = get_row_pruning(sub_param_dict)
22
+ output[HEAD_PRUNING] = get_head_pruning(sub_param_dict)
23
+ output[CHANNEL_PRUNING] = get_channel_pruning(sub_param_dict)
24
+
25
+ output[LAYER_REDUCTION] = get_layer_reduction(sub_param_dict)
26
+
27
+ return output
28
+
29
+
30
+ def get_layer_reduction(param_dict):
31
+ output = {}
32
+ output[LAYER_REDUCTION_ENABLED] = LAYER_REDUCTION_ENABLED_DEFAULT
33
+ if get_layer_reduction_enabled(param_dict):
34
+ output[LAYER_REDUCTION_ENABLED] = get_layer_reduction_enabled(param_dict)
35
+ for key, val in get_layer_reduction_params(param_dict).items():
36
+ output[key] = val
37
+ return output
38
+
39
+
40
+ def get_layer_reduction_enabled(param_dict):
41
+ if LAYER_REDUCTION in param_dict.keys():
42
+ return get_scalar_param(param_dict[LAYER_REDUCTION], LAYER_REDUCTION_ENABLED, LAYER_REDUCTION_ENABLED_DEFAULT)
43
+ else:
44
+ return False
45
+
46
+
47
+ def get_layer_reduction_params(param_dict):
48
+ if LAYER_REDUCTION in param_dict.keys():
49
+ layer_reduction_params = copy.copy(param_dict[LAYER_REDUCTION])
50
+ layer_reduction_params.pop(LAYER_REDUCTION_ENABLED)
51
+ return layer_reduction_params
52
+ else:
53
+ return False
54
+
55
+
56
+ def get_quantize_enabled(param_dict):
57
+ if COMPRESSION_TRAINING not in param_dict.keys():
58
+ return False
59
+
60
+ sub_param_dict = param_dict[COMPRESSION_TRAINING]
61
+ output = get_weight_quantization_shared_parameters(sub_param_dict)
62
+ return output[WEIGHT_QUANTIZE_ENABLED]
63
+
64
+
65
+ def get_weight_quantization(param_dict):
66
+ output = {}
67
+ if WEIGHT_QUANTIZATION not in param_dict.keys():
68
+ param_dict[WEIGHT_QUANTIZATION] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
69
+ sub_param_dict = param_dict[WEIGHT_QUANTIZATION]
70
+ # shared parameters
71
+ output[SHARED_PARAMETERS] = get_weight_quantization_shared_parameters(sub_param_dict)
72
+ # each sub-groups
73
+ if output[SHARED_PARAMETERS][WEIGHT_QUANTIZE_ENABLED]:
74
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
75
+ ), f"Weigh Quantization is enabled, {DIFFERENT_GROUPS} must be specified"
76
+ output[DIFFERENT_GROUPS] = get_weight_quantization_different_groups(sub_param_dict)
77
+ return output
78
+
79
+
80
+ def get_weight_quantization_shared_parameters(param_dict):
81
+ output = {}
82
+ if SHARED_PARAMETERS in param_dict.keys():
83
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
84
+ output[WEIGHT_QUANTIZE_ENABLED] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_ENABLED,
85
+ WEIGHT_QUANTIZE_ENABLED_DEFAULT)
86
+ output[WEIGHT_QUANTIZE_KERNEL] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_KERNEL,
87
+ WEIGHT_QUANTIZE_KERNEL_DEFAULT)
88
+ output[WEIGHT_QUANTIZE_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_SCHEDULE_OFFSET,
89
+ WEIGHT_QUANTIZE_SCHEDULE_OFFSET_DEFAULT)
90
+ output[WEIGHT_QUANTIZE_GROUPS] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_GROUPS,
91
+ WEIGHT_QUANTIZE_GROUPS_DEFAULT)
92
+ output[WEIGHT_QUANTIZE_VERBOSE] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_VERBOSE,
93
+ WEIGHT_QUANTIZE_VERBOSE_DEFAULT)
94
+ output[WEIGHT_QUANTIZE_TYPE] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_TYPE,
95
+ WEIGHT_QUANTIZE_TYPE_DEFAULT)
96
+ output[WEIGHT_QUANTIZE_IN_FORWARD_ENABLED] = get_scalar_param(sub_param_dict,
97
+ WEIGHT_QUANTIZE_IN_FORWARD_ENABLED,
98
+ WEIGHT_QUANTIZE_IN_FORWARD_ENABLED_DEFAULT)
99
+ assert output[WEIGHT_QUANTIZE_TYPE] in [
100
+ WEIGHT_QUANTIZE_SYMMETRIC, WEIGHT_QUANTIZE_ASYMMETRIC
101
+ ], f"Invalid weight quantize type. Supported types: [{WEIGHT_QUANTIZE_SYMMETRIC}, {WEIGHT_QUANTIZE_ASYMMETRIC}]"
102
+ output[WEIGHT_QUANTIZE_ROUNDING] = get_scalar_param(sub_param_dict, WEIGHT_QUANTIZE_ROUNDING,
103
+ WEIGHT_QUANTIZE_ROUNDING_DEFAULT)
104
+ assert output[WEIGHT_QUANTIZE_ROUNDING] in [
105
+ WEIGHT_QUANTIZE_NEAREST_ROUNDING, WEIGHT_QUANTIZE_STOCHASTIC_ROUNDING
106
+ ], f"Invalid weight quantize rounding. Supported types: [{WEIGHT_QUANTIZE_NEAREST_ROUNDING}, {WEIGHT_QUANTIZE_STOCHASTIC_ROUNDING}]"
107
+ if WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE in sub_param_dict.keys():
108
+ output[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE] = get_scalar_param(
109
+ sub_param_dict[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE], WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED,
110
+ WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED_DEFAULT)
111
+ output[WEIGHT_QUANTIZE_CHANGE_RATIO] = get_scalar_param(
112
+ sub_param_dict[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE], WEIGHT_QUANTIZE_CHANGE_RATIO,
113
+ WEIGHT_QUANTIZE_CHANGE_RATIO_DEFAULT)
114
+ else:
115
+ output[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE] = WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED_DEFAULT
116
+ output[WEIGHT_QUANTIZE_CHANGE_RATIO] = WEIGHT_QUANTIZE_CHANGE_RATIO_DEFAULT
117
+ else:
118
+ output[WEIGHT_QUANTIZE_ENABLED] = WEIGHT_QUANTIZE_ENABLED_DEFAULT
119
+ output[WEIGHT_QUANTIZE_KERNEL] = WEIGHT_QUANTIZE_KERNEL_DEFAULT
120
+ output[WEIGHT_QUANTIZE_SCHEDULE_OFFSET] = WEIGHT_QUANTIZE_SCHEDULE_OFFSET_DEFAULT
121
+ output[WEIGHT_QUANTIZE_GROUPS] = WEIGHT_QUANTIZE_GROUPS_DEFAULT
122
+ output[WEIGHT_QUANTIZE_VERBOSE] = WEIGHT_QUANTIZE_VERBOSE_DEFAULT
123
+ output[WEIGHT_QUANTIZE_TYPE] = WEIGHT_QUANTIZE_TYPE_DEFAULT
124
+ output[WEIGHT_QUANTIZE_ROUNDING] = WEIGHT_QUANTIZE_ROUNDING_DEFAULT
125
+ output[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE] = WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED_DEFAULT
126
+ output[WEIGHT_QUANTIZE_CHANGE_RATIO] = WEIGHT_QUANTIZE_CHANGE_RATIO_DEFAULT
127
+ return output
128
+
129
+
130
+ def get_weight_quantization_different_groups(param_dict):
131
+ output = {}
132
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
133
+
134
+ def get_params(name, group_dict):
135
+ assert WEIGHT_QUANTIZE_START_BITS in group_dict.keys(
136
+ ), f"{WEIGHT_QUANTIZE_START_BITS} must be specified for weight quantization group {name}"
137
+ assert WEIGHT_QUANTIZE_TARGET_BITS in group_dict.keys(
138
+ ), f"{WEIGHT_QUANTIZE_TARGET_BITS} must be specified for weight quantization group {name}"
139
+ group_dict[WEIGHT_QUANTIZATION_PERIOD] = get_scalar_param(group_dict, WEIGHT_QUANTIZATION_PERIOD,
140
+ WEIGHT_QUANTIZATION_PERIOD_DEFAULT)
141
+ return group_dict
142
+
143
+ for k, v in sub_param_dict.items():
144
+ output[k] = {}
145
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
146
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
147
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
148
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
149
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
150
+
151
+ return output
152
+
153
+
154
+ def get_activation_quantization(param_dict):
155
+ output = {}
156
+ if ACTIVATION_QUANTIZATION not in param_dict.keys():
157
+ param_dict[ACTIVATION_QUANTIZATION] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
158
+ sub_param_dict = param_dict[ACTIVATION_QUANTIZATION]
159
+ # shared parameters
160
+ output[SHARED_PARAMETERS] = get_activation_quantization_shared_parameters(sub_param_dict)
161
+ # each sub-groups
162
+ if output[SHARED_PARAMETERS][ACTIVATION_QUANTIZATION_ENABLED]:
163
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
164
+ ), f"Activation Quantization is enabled, {DIFFERENT_GROUPS} must be specified"
165
+ output[DIFFERENT_GROUPS] = get_activation_quantization_different_groups(sub_param_dict)
166
+ return output
167
+
168
+
169
+ def get_activation_quantization_shared_parameters(param_dict):
170
+ output = {}
171
+ if SHARED_PARAMETERS in param_dict.keys():
172
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
173
+ output[ACTIVATION_QUANTIZATION_ENABLED] = get_scalar_param(sub_param_dict, ACTIVATION_QUANTIZATION_ENABLED,
174
+ ACTIVATION_QUANTIZATION_ENABLED_DEFAULT)
175
+ output[ACTIVATION_QUANTIZE_TYPE] = get_scalar_param(sub_param_dict, ACTIVATION_QUANTIZE_TYPE,
176
+ ACTIVATION_QUANTIZE_TYPE_DEFAULT)
177
+ assert output[ACTIVATION_QUANTIZE_TYPE] in [
178
+ ACTIVATION_QUANTIZE_SYMMETRIC, ACTIVATION_QUANTIZE_ASYMMETRIC
179
+ ], f"Invalid activation quantize type. Supported types: [{ACTIVATION_QUANTIZE_SYMMETRIC}, {ACTIVATION_QUANTIZE_ASYMMETRIC}]"
180
+ output[ACTIVATION_QUANTIZE_RANGE] = get_scalar_param(sub_param_dict, ACTIVATION_QUANTIZE_RANGE,
181
+ ACTIVATION_QUANTIZE_RANGE_DEFAULT)
182
+ assert output[ACTIVATION_QUANTIZE_RANGE] in [
183
+ ACTIVATION_QUANTIZE_RANGE_DYNAMIC, ACTIVATION_QUANTIZE_RANGE_STATIC
184
+ ], f"Invalid activation quantize range calibration. Supported types: [{ACTIVATION_QUANTIZE_RANGE_DYNAMIC}, {ACTIVATION_QUANTIZE_RANGE_STATIC}]"
185
+ output[ACTIVATION_QUANTIZE_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict,
186
+ ACTIVATION_QUANTIZE_SCHEDULE_OFFSET,
187
+ ACTIVATION_QUANTIZE_SCHEDULE_OFFSET_DEFAULT)
188
+ else:
189
+ output[ACTIVATION_QUANTIZATION_ENABLED] = ACTIVATION_QUANTIZATION_ENABLED_DEFAULT
190
+ output[ACTIVATION_QUANTIZE_TYPE] = ACTIVATION_QUANTIZE_TYPE_DEFAULT
191
+ output[ACTIVATION_QUANTIZE_RANGE] = ACTIVATION_QUANTIZE_RANGE_DEFAULT
192
+ output[ACTIVATION_QUANTIZE_SCHEDULE_OFFSET] = ACTIVATION_QUANTIZE_SCHEDULE_OFFSET_DEFAULT
193
+ return output
194
+
195
+
196
+ def get_activation_quantization_different_groups(param_dict):
197
+ output = {}
198
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
199
+
200
+ def get_params(name, group_dict):
201
+ assert ACTIVATION_QUANTIZE_BITS in group_dict.keys(
202
+ ), f"{ACTIVATION_QUANTIZE_BITS} must be specified for activation quantization group {name}"
203
+ return group_dict
204
+
205
+ for k, v in sub_param_dict.items():
206
+ output[k] = {}
207
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
208
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
209
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
210
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
211
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
212
+
213
+ return output
214
+
215
+
216
+ def get_sparse_pruning(param_dict):
217
+ output = {}
218
+ if SPARSE_PRUNING not in param_dict.keys():
219
+ param_dict[SPARSE_PRUNING] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
220
+ sub_param_dict = param_dict[SPARSE_PRUNING]
221
+ # shared parameters
222
+ output[SHARED_PARAMETERS] = get_sparse_pruning_shared_parameters(sub_param_dict)
223
+ # each sub-groups
224
+ if output[SHARED_PARAMETERS][SPARSE_PRUNING_ENABLED] and output[SHARED_PARAMETERS][
225
+ SPARSE_PRUNING_METHOD] != SPARSE_PRUNING_METHOD_SNIP_MOMENTUM:
226
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
227
+ ), f"Sparse Pruning is enabled and not snip_momentum method, {DIFFERENT_GROUPS} must be specified"
228
+ output[DIFFERENT_GROUPS] = get_sparse_pruning_different_groups(sub_param_dict)
229
+ return output
230
+
231
+
232
+ def get_sparse_pruning_shared_parameters(param_dict):
233
+ output = {}
234
+
235
+ if SHARED_PARAMETERS in param_dict.keys():
236
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
237
+ output[SPARSE_PRUNING_ENABLED] = get_scalar_param(sub_param_dict, SPARSE_PRUNING_ENABLED,
238
+ SPARSE_PRUNING_ENABLED_DEFAULT)
239
+ output[SPARSE_PRUNING_METHOD] = get_scalar_param(sub_param_dict, SPARSE_PRUNING_METHOD,
240
+ SPARSE_PRUNING_METHOD_DEFAULT)
241
+ assert output[SPARSE_PRUNING_METHOD] in [
242
+ SPARSE_PRUNING_METHOD_L1, SPARSE_PRUNING_METHOD_TOPK, SPARSE_PRUNING_METHOD_SNIP_MOMENTUM
243
+ ], f"Invalid sparse pruning method. Supported types: [{SPARSE_PRUNING_METHOD_L1}, {SPARSE_PRUNING_METHOD_TOPK}, {SPARSE_PRUNING_METHOD_SNIP_MOMENTUM}]"
244
+ output[SPARSE_PRUNING_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict, SPARSE_PRUNING_SCHEDULE_OFFSET,
245
+ SPARSE_PRUNING_SCHEDULE_OFFSET_DEFAULT)
246
+ if output[SPARSE_PRUNING_METHOD] == SPARSE_PRUNING_METHOD_SNIP_MOMENTUM:
247
+ output[SPARSE_PRUNING_BLOCK_PATTERN] = get_scalar_param(sub_param_dict, SPARSE_PRUNING_BLOCK_PATTERN,
248
+ SPARSE_PRUNING_BLOCK_PATTERN_DEFAULT)
249
+ output[SPARSE_PRUNING_DENSE_RATIO] = get_scalar_param(sub_param_dict, SPARSE_PRUNING_DENSE_RATIO,
250
+ SPARSE_PRUNING_DENSE_RATIO_DEFAULT)
251
+ assert output[SPARSE_PRUNING_DENSE_RATIO] > 0 and output[
252
+ SPARSE_PRUNING_DENSE_RATIO] < 1, f"Invalid dense_ratio value. Must be less than 1"
253
+ output[SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE] = get_scalar_param(
254
+ sub_param_dict, SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE, SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE_DEFAULT)
255
+ output[SPARSE_PRUNING_EXCLUDED_MODULES] = get_list_param(sub_param_dict, SPARSE_PRUNING_EXCLUDED_MODULES,
256
+ SPARSE_PRUNING_EXCLUDED_MODULES_DEFAULT)
257
+ output[SPARSE_PRUNING_SCHEDULE_OFFSET_END] = get_scalar_param(sub_param_dict,
258
+ SPARSE_PRUNING_SCHEDULE_OFFSET_END,
259
+ output[SPARSE_PRUNING_SCHEDULE_OFFSET])
260
+ assert output[SPARSE_PRUNING_SCHEDULE_OFFSET] <= output[
261
+ SPARSE_PRUNING_SCHEDULE_OFFSET_END], f"Invalid schedule_offset and schedule_offset_end values"
262
+ else:
263
+ output[SPARSE_PRUNING_ENABLED] = SPARSE_PRUNING_ENABLED_DEFAULT
264
+ output[SPARSE_PRUNING_METHOD] = SPARSE_PRUNING_METHOD_DEFAULT
265
+ output[SPARSE_PRUNING_SCHEDULE_OFFSET] = SPARSE_PRUNING_SCHEDULE_OFFSET_DEFAULT
266
+ return output
267
+
268
+
269
+ def get_sparse_pruning_different_groups(param_dict):
270
+ output = {}
271
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
272
+
273
+ def get_params(name, group_dict):
274
+ assert SPARSE_PRUNING_DENSE_RATIO in group_dict.keys(
275
+ ), f"{SPARSE_PRUNING_DENSE_RATIO} must be specified for sparse pruning group {name}"
276
+ return group_dict
277
+
278
+ for k, v in sub_param_dict.items():
279
+ output[k] = {}
280
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
281
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
282
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
283
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
284
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
285
+
286
+ return output
287
+
288
+
289
+ def get_row_pruning(param_dict):
290
+ output = {}
291
+ if ROW_PRUNING not in param_dict.keys():
292
+ param_dict[ROW_PRUNING] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
293
+ sub_param_dict = param_dict[ROW_PRUNING]
294
+ # shared parameters
295
+ output[SHARED_PARAMETERS] = get_row_pruning_shared_parameters(sub_param_dict)
296
+ # each sub-groups
297
+ if output[SHARED_PARAMETERS][ROW_PRUNING_ENABLED]:
298
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
299
+ ), f"Row Pruning is enabled, {DIFFERENT_GROUPS} must be specified"
300
+ output[DIFFERENT_GROUPS] = get_row_pruning_different_groups(sub_param_dict)
301
+ return output
302
+
303
+
304
+ def get_row_pruning_shared_parameters(param_dict):
305
+ output = {}
306
+ if SHARED_PARAMETERS in param_dict.keys():
307
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
308
+ output[ROW_PRUNING_ENABLED] = get_scalar_param(sub_param_dict, ROW_PRUNING_ENABLED,
309
+ ROW_PRUNING_ENABLED_DEFAULT)
310
+ output[ROW_PRUNING_METHOD] = get_scalar_param(sub_param_dict, ROW_PRUNING_METHOD, ROW_PRUNING_METHOD_DEFAULT)
311
+ assert output[ROW_PRUNING_METHOD] in [
312
+ ROW_PRUNING_METHOD_L1, ROW_PRUNING_METHOD_TOPK
313
+ ], f"Invalid row pruning method. Supported types: [{ROW_PRUNING_METHOD_L1}, {ROW_PRUNING_METHOD_TOPK}]"
314
+ output[ROW_PRUNING_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict, ROW_PRUNING_SCHEDULE_OFFSET,
315
+ ROW_PRUNING_SCHEDULE_OFFSET_DEFAULT)
316
+ else:
317
+ output[ROW_PRUNING_ENABLED] = ROW_PRUNING_ENABLED_DEFAULT
318
+ output[ROW_PRUNING_METHOD] = ROW_PRUNING_METHOD_DEFAULT
319
+ output[ROW_PRUNING_SCHEDULE_OFFSET] = ROW_PRUNING_SCHEDULE_OFFSET_DEFAULT
320
+ return output
321
+
322
+
323
+ def get_row_pruning_different_groups(param_dict):
324
+ output = {}
325
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
326
+
327
+ def get_params(name, group_dict):
328
+ assert ROW_PRUNING_DENSE_RATIO in group_dict.keys(
329
+ ), f"{ROW_PRUNING_DENSE_RATIO} must be specified for row pruning group {name}"
330
+ return group_dict
331
+
332
+ for k, v in sub_param_dict.items():
333
+ output[k] = {}
334
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
335
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
336
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
337
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
338
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
339
+ return output
340
+
341
+
342
+ def get_head_pruning(param_dict):
343
+ output = {}
344
+ if HEAD_PRUNING not in param_dict.keys():
345
+ param_dict[HEAD_PRUNING] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
346
+ sub_param_dict = param_dict[HEAD_PRUNING]
347
+ # shared parameters
348
+ output[SHARED_PARAMETERS] = get_head_pruning_shared_parameters(sub_param_dict)
349
+ # each sub-groups
350
+ if output[SHARED_PARAMETERS][HEAD_PRUNING_ENABLED]:
351
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
352
+ ), f"Head Pruning is enabled, {DIFFERENT_GROUPS} must be specified"
353
+ output[DIFFERENT_GROUPS] = get_head_pruning_different_groups(sub_param_dict)
354
+ return output
355
+
356
+
357
+ def get_head_pruning_shared_parameters(param_dict):
358
+ output = {}
359
+ if SHARED_PARAMETERS in param_dict.keys():
360
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
361
+ output[HEAD_PRUNING_ENABLED] = get_scalar_param(sub_param_dict, HEAD_PRUNING_ENABLED,
362
+ HEAD_PRUNING_ENABLED_DEFAULT)
363
+ output[HEAD_PRUNING_METHOD] = get_scalar_param(sub_param_dict, HEAD_PRUNING_METHOD,
364
+ HEAD_PRUNING_METHOD_DEFAULT)
365
+ assert output[HEAD_PRUNING_METHOD] in [
366
+ HEAD_PRUNING_METHOD_L1, HEAD_PRUNING_METHOD_TOPK
367
+ ], f"Invalid head pruning method. Supported types: [{HEAD_PRUNING_METHOD_L1}, {HEAD_PRUNING_METHOD_TOPK}]"
368
+ output[HEAD_PRUNING_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict, HEAD_PRUNING_SCHEDULE_OFFSET,
369
+ HEAD_PRUNING_SCHEDULE_OFFSET_DEFAULT)
370
+ if output[HEAD_PRUNING_ENABLED]:
371
+ assert HEAD_PRUNING_NUM_HEADS in sub_param_dict.keys(
372
+ ), f"{HEAD_PRUNING_NUM_HEADS} must be specified for head pruning"
373
+ output[HEAD_PRUNING_NUM_HEADS] = sub_param_dict[HEAD_PRUNING_NUM_HEADS]
374
+ else:
375
+ output[HEAD_PRUNING_ENABLED] = HEAD_PRUNING_ENABLED_DEFAULT
376
+ output[HEAD_PRUNING_METHOD] = HEAD_PRUNING_METHOD_DEFAULT
377
+ output[HEAD_PRUNING_SCHEDULE_OFFSET] = HEAD_PRUNING_SCHEDULE_OFFSET_DEFAULT
378
+ return output
379
+
380
+
381
+ def get_head_pruning_different_groups(param_dict):
382
+ output = {}
383
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
384
+
385
+ def get_params(name, group_dict):
386
+ assert HEAD_PRUNING_DENSE_RATIO in group_dict.keys(
387
+ ), f"dense_ratio must be specified for head pruning group {name}"
388
+ return group_dict
389
+
390
+ for k, v in sub_param_dict.items():
391
+ output[k] = {}
392
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
393
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
394
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
395
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
396
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
397
+ return output
398
+
399
+
400
+ def get_channel_pruning(param_dict):
401
+ output = {}
402
+ if CHANNEL_PRUNING not in param_dict.keys():
403
+ param_dict[CHANNEL_PRUNING] = {SHARED_PARAMETERS: {}, DIFFERENT_GROUPS: {}}
404
+ sub_param_dict = param_dict[CHANNEL_PRUNING]
405
+ # shared parameters
406
+ output[SHARED_PARAMETERS] = get_channel_pruning_shared_parameters(sub_param_dict)
407
+ # each sub-groups
408
+ if output[SHARED_PARAMETERS][CHANNEL_PRUNING_ENABLED]:
409
+ assert DIFFERENT_GROUPS in sub_param_dict.keys(
410
+ ), f"Sparse Pruning is enabled, {DIFFERENT_GROUPS} must be specified"
411
+ output[DIFFERENT_GROUPS] = get_channel_pruning_different_groups(sub_param_dict)
412
+ return output
413
+
414
+
415
+ def get_channel_pruning_shared_parameters(param_dict):
416
+ output = {}
417
+ if SHARED_PARAMETERS in param_dict.keys():
418
+ sub_param_dict = param_dict[SHARED_PARAMETERS]
419
+ output[CHANNEL_PRUNING_ENABLED] = get_scalar_param(sub_param_dict, CHANNEL_PRUNING_ENABLED,
420
+ CHANNEL_PRUNING_ENABLED_DEFAULT)
421
+ output[CHANNEL_PRUNING_METHOD] = get_scalar_param(sub_param_dict, CHANNEL_PRUNING_METHOD,
422
+ CHANNEL_PRUNING_METHOD_DEFAULT)
423
+ assert output[CHANNEL_PRUNING_METHOD] in [
424
+ CHANNEL_PRUNING_METHOD_L1, CHANNEL_PRUNING_METHOD_TOPK
425
+ ], f"Invalid channel pruning method. Supported types: [{CHANNEL_PRUNING_METHOD_L1}, {CHANNEL_PRUNING_METHOD_TOPK}]"
426
+ output[CHANNEL_PRUNING_SCHEDULE_OFFSET] = get_scalar_param(sub_param_dict, CHANNEL_PRUNING_SCHEDULE_OFFSET,
427
+ CHANNEL_PRUNING_SCHEDULE_OFFSET_DEFAULT)
428
+ else:
429
+ output[CHANNEL_PRUNING_ENABLED] = CHANNEL_PRUNING_ENABLED_DEFAULT
430
+ output[CHANNEL_PRUNING_METHOD] = CHANNEL_PRUNING_METHOD_DEFAULT
431
+ output[CHANNEL_PRUNING_SCHEDULE_OFFSET] = CHANNEL_PRUNING_SCHEDULE_OFFSET_DEFAULT
432
+ return output
433
+
434
+
435
+ def get_channel_pruning_different_groups(param_dict):
436
+ output = {}
437
+ sub_param_dict = param_dict[DIFFERENT_GROUPS]
438
+
439
+ def get_params(name, group_dict):
440
+ assert CHANNEL_PRUNING_DENSE_RATIO in group_dict.keys(
441
+ ), f"{CHANNEL_PRUNING_DENSE_RATIO} must be specified for channel pruning group {name}"
442
+ return group_dict
443
+
444
+ for k, v in sub_param_dict.items():
445
+ output[k] = {}
446
+ output[k][DIFFERENT_GROUPS_PARAMETERS] = get_params(k, sub_param_dict[k][DIFFERENT_GROUPS_PARAMETERS])
447
+ output[k][DIFFERENT_GROUPS_MODULE_SCOPE] = get_scalar_param(sub_param_dict[k], DIFFERENT_GROUPS_MODULE_SCOPE,
448
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT)
449
+ output[k][DIFFERENT_GROUPS_RELATED_MODULE_SCOPE] = get_scalar_param(
450
+ sub_param_dict[k], DIFFERENT_GROUPS_RELATED_MODULE_SCOPE, DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT)
451
+
452
+ return output
lib/python3.12/site-packages/deepspeed/compression/constants.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ #########################################
7
+ # Compression Methods
8
+ # It has several sub-components
9
+ # #########################################
10
+ COMPRESSION_TRAINING = "compression_training"
11
+ SHARED_PARAMETERS = "shared_parameters"
12
+ DIFFERENT_GROUPS = "different_groups"
13
+ TECHNIQUE_ENABLED = "enabled"
14
+ TECHNIQUE_SCHEDULE_OFFSET = "schedule_offset"
15
+ TECHNIQUE_SCHEDULE_OFFSET_END = "schedule_offset_end"
16
+ DIFFERENT_GROUPS_PARAMETERS = "params"
17
+ DIFFERENT_GROUPS_MODULE_SCOPE = "modules"
18
+ DIFFERENT_GROUPS_MODULE_SCOPE_DEFAULT = "*"
19
+ DIFFERENT_GROUPS_RELATED_MODULE_SCOPE = "related_modules"
20
+ DIFFERENT_GROUPS_RELATED_MODULE_SCOPE_DEFAULT = None
21
+ # COMPRESSION_TRAINING_ENABLED = "enabled"
22
+ # COMPRESSION_TRAINING_ENABLED_DEFAULT = False
23
+
24
+ ####
25
+ # Layer Reduction
26
+ ####
27
+ LAYER_REDUCTION = "layer_reduction"
28
+ LAYER_REDUCTION_ENABLED = "enabled"
29
+ LAYER_REDUCTION_ENABLED_DEFAULT = False
30
+ KEEP_NUMBER_LAYER = "keep_number_layer"
31
+ MODULE_NAME_PREFIX = "module_name_prefix"
32
+ TEACHER_LAYER = "teacher_layer"
33
+ OTHER_MODULE_NAME = "other_module_name"
34
+
35
+ ####
36
+ # Weight Quantization
37
+ ####
38
+ WEIGHT_QUANTIZATION = "weight_quantization"
39
+
40
+ WEIGHT_QUANTIZATION_PERIOD = "quantization_period"
41
+ WEIGHT_QUANTIZATION_PERIOD_DEFAULT = 1
42
+
43
+ WEIGHT_QUANTIZE_IN_FORWARD_ENABLED = "quantize_weight_in_forward"
44
+ WEIGHT_QUANTIZE_IN_FORWARD_ENABLED_DEFAULT = False
45
+
46
+ WEIGHT_QUANTIZE_ENABLED = TECHNIQUE_ENABLED
47
+ WEIGHT_QUANTIZE_ENABLED_DEFAULT = False
48
+
49
+ WEIGHT_QUANTIZE_KERNEL = "quantizer_kernel"
50
+ WEIGHT_QUANTIZE_KERNEL_DEFAULT = False
51
+
52
+ WEIGHT_QUANTIZE_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
53
+ WEIGHT_QUANTIZE_SCHEDULE_OFFSET_DEFAULT = 0
54
+
55
+ WEIGHT_QUANTIZE_GROUPS = "quantize_groups"
56
+ WEIGHT_QUANTIZE_GROUPS_DEFAULT = 1
57
+
58
+ WEIGHT_QUANTIZE_VERBOSE = "quantize_verbose"
59
+ WEIGHT_QUANTIZE_VERBOSE_DEFAULT = False
60
+
61
+ WEIGHT_QUANTIZE_TYPE = "quantization_type"
62
+ WEIGHT_QUANTIZE_TYPE_DEFAULT = "symmetric"
63
+ WEIGHT_QUANTIZE_SYMMETRIC = "symmetric"
64
+ WEIGHT_QUANTIZE_ASYMMETRIC = "asymmetric"
65
+
66
+ WEIGHT_QUANTIZE_ROUNDING = "rounding"
67
+ WEIGHT_QUANTIZE_ROUNDING_DEFAULT = "nearest"
68
+ WEIGHT_QUANTIZE_STOCHASTIC_ROUNDING = "stochastic"
69
+ WEIGHT_QUANTIZE_NEAREST_ROUNDING = "nearest"
70
+ # maybe deleted for a cleaner version
71
+ WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE = "fp16_mixed_quantize"
72
+
73
+ WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED = "enabled"
74
+ WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE_ENABLED_DEFAULT = False
75
+
76
+ WEIGHT_QUANTIZE_CHANGE_RATIO = "quantize_change_ratio"
77
+ WEIGHT_QUANTIZE_CHANGE_RATIO_DEFAULT = 0.001
78
+
79
+ WEIGHT_QUANTIZE_START_BITS = "start_bits"
80
+ WEIGHT_QUANTIZE_TARGET_BITS = "target_bits"
81
+ ###
82
+ # Activation Quantization
83
+ ###
84
+ ACTIVATION_QUANTIZATION = "activation_quantization"
85
+
86
+ ACTIVATION_QUANTIZATION_ENABLED = TECHNIQUE_ENABLED
87
+ ACTIVATION_QUANTIZATION_ENABLED_DEFAULT = False
88
+
89
+ ACTIVATION_QUANTIZE_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
90
+ ACTIVATION_QUANTIZE_SCHEDULE_OFFSET_DEFAULT = 1000
91
+
92
+ ACTIVATION_QUANTIZE_TYPE = "quantization_type"
93
+ ACTIVATION_QUANTIZE_TYPE_DEFAULT = "symmetric"
94
+ ACTIVATION_QUANTIZE_SYMMETRIC = "symmetric"
95
+ ACTIVATION_QUANTIZE_ASYMMETRIC = "asymmetric"
96
+
97
+ ACTIVATION_QUANTIZE_RANGE = 'range_calibration'
98
+ ACTIVATION_QUANTIZE_RANGE_DEFAULT = 'dynamic'
99
+ ACTIVATION_QUANTIZE_RANGE_STATIC = 'static'
100
+ ACTIVATION_QUANTIZE_RANGE_DYNAMIC = 'dynamic'
101
+
102
+ ACTIVATION_QUANTIZE_BITS = "bits"
103
+ ###
104
+ # Sparse Pruning
105
+ ###
106
+ SPARSE_PRUNING = "sparse_pruning"
107
+
108
+ SPARSE_PRUNING_ENABLED = TECHNIQUE_ENABLED
109
+ SPARSE_PRUNING_ENABLED_DEFAULT = False
110
+
111
+ SPARSE_PRUNING_METHOD = "method"
112
+ SPARSE_PRUNING_METHOD_DEFAULT = "l1"
113
+ SPARSE_PRUNING_METHOD_L1 = "l1"
114
+ SPARSE_PRUNING_METHOD_TOPK = "topk"
115
+ SPARSE_PRUNING_METHOD_SNIP_MOMENTUM = "snip_momentum"
116
+
117
+ SPARSE_PRUNING_BLOCK_PATTERN = "block_pattern"
118
+ SPARSE_PRUNING_BLOCK_PATTERN_DEFAULT = "4x1"
119
+
120
+ SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE = "schedule_offset_stride"
121
+ SPARSE_PRUNING_SCHEDULE_OFFSET_STRIDE_DEFAULT = 1
122
+
123
+ SPARSE_PRUNING_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
124
+ SPARSE_PRUNING_SCHEDULE_OFFSET_DEFAULT = 1000
125
+
126
+ SPARSE_PRUNING_SCHEDULE_OFFSET_END = TECHNIQUE_SCHEDULE_OFFSET_END
127
+ SPARSE_PRUNING_SCHEDULE_OFFSET_END_DEFAULT = SPARSE_PRUNING_SCHEDULE_OFFSET_DEFAULT
128
+
129
+ SPARSE_PRUNING_DENSE_RATIO = "dense_ratio"
130
+ SPARSE_PRUNING_DENSE_RATIO_DEFAULT = 0.1
131
+
132
+ SPARSE_PRUNING_EXCLUDED_MODULES = "excluded_modules"
133
+ SPARSE_PRUNING_EXCLUDED_MODULES_DEFAULT = []
134
+ ###
135
+ # Row Pruning
136
+ ###
137
+ ROW_PRUNING = "row_pruning"
138
+
139
+ ROW_PRUNING_ENABLED = TECHNIQUE_ENABLED
140
+ ROW_PRUNING_ENABLED_DEFAULT = False
141
+
142
+ ROW_PRUNING_METHOD = "method"
143
+ ROW_PRUNING_METHOD_DEFAULT = "l1"
144
+ ROW_PRUNING_METHOD_L1 = "l1"
145
+ ROW_PRUNING_METHOD_TOPK = "topk"
146
+
147
+ ROW_PRUNING_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
148
+ ROW_PRUNING_SCHEDULE_OFFSET_DEFAULT = 1000
149
+
150
+ ROW_PRUNING_DENSE_RATIO = "dense_ratio"
151
+
152
+ ###
153
+ # Head Pruning
154
+ ###
155
+ HEAD_PRUNING = "head_pruning"
156
+
157
+ HEAD_PRUNING_ENABLED = TECHNIQUE_ENABLED
158
+ HEAD_PRUNING_ENABLED_DEFAULT = False
159
+
160
+ HEAD_PRUNING_METHOD = "method"
161
+ HEAD_PRUNING_METHOD_DEFAULT = "topk"
162
+ HEAD_PRUNING_METHOD_L1 = "l1"
163
+ HEAD_PRUNING_METHOD_TOPK = "topk"
164
+
165
+ HEAD_PRUNING_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
166
+ HEAD_PRUNING_SCHEDULE_OFFSET_DEFAULT = 1000
167
+
168
+ HEAD_PRUNING_NUM_HEADS = "num_heads"
169
+
170
+ HEAD_PRUNING_DENSE_RATIO = "dense_ratio"
171
+
172
+ ###
173
+ # Channel Pruning
174
+ ###
175
+ CHANNEL_PRUNING = "channel_pruning"
176
+
177
+ CHANNEL_PRUNING_ENABLED = TECHNIQUE_ENABLED
178
+ CHANNEL_PRUNING_ENABLED_DEFAULT = False
179
+
180
+ CHANNEL_PRUNING_METHOD = "method"
181
+ CHANNEL_PRUNING_METHOD_DEFAULT = "l1"
182
+ CHANNEL_PRUNING_METHOD_L1 = "l1"
183
+ CHANNEL_PRUNING_METHOD_TOPK = "topk"
184
+
185
+ CHANNEL_PRUNING_SCHEDULE_OFFSET = TECHNIQUE_SCHEDULE_OFFSET
186
+ CHANNEL_PRUNING_SCHEDULE_OFFSET_DEFAULT = 1000
187
+
188
+ CHANNEL_PRUNING_DENSE_RATIO = "dense_ratio"
lib/python3.12/site-packages/deepspeed/compression/helper.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ import torch
7
+ from .basic_layer import Embedding_Compress, LinearLayer_Compress, Conv2dLayer_Compress, BNLayer_Compress, ColumnParallelLinear_Compress, RowParallelLinear_Compress
8
+ from .constants import *
9
+ from deepspeed.utils import logger
10
+
11
+ try:
12
+ from neural_compressor.compression import pruner as nc_pruner
13
+ except ImportError as e:
14
+ nc_pruner = None
15
+
16
+
17
+ def recursive_getattr(model, module_name):
18
+ """
19
+ Recursively get the attribute of a module.
20
+ Args:
21
+ model (`torch.nn.Module`)
22
+ The model to get the attribute from.
23
+ module_name (`str`)
24
+ The name of the module to get the attribute from.
25
+ """
26
+ split_list = module_name.split('.')
27
+ output = model
28
+ for name in split_list:
29
+ output = getattr(output, name)
30
+ return output
31
+
32
+
33
+ def recursive_setattr(model, module_name, module):
34
+ """
35
+ Recursively set the attribute of a module.
36
+ Args:
37
+ model (`torch.nn.Module`)
38
+ The model to set the attribute in.
39
+ module_name (`str`)
40
+ The name of the module to set the attribute in.
41
+ module (`torch.nn.Module`)
42
+ The module to set the attribute to.
43
+ """
44
+ split_list = module_name.split('.')
45
+ output = model
46
+ for name in split_list[:-1]:
47
+ output = getattr(output, name)
48
+ output.__setattr__(split_list[-1], module)
49
+
50
+
51
+ def module_replacement(model, module_name, compression_technique=None, mpu=None):
52
+ """
53
+ Replace a module with a new module.
54
+ Args:
55
+ model (`torch.nn.Module`)
56
+ The model to replace the module in.
57
+ module_name (`str`)
58
+ The name of the module to replace.
59
+ compression_technique (`str`)
60
+ The compression technique to use for the new module.
61
+ """
62
+
63
+ # Get the old module
64
+ old_module = recursive_getattr(model, module_name)
65
+
66
+ need_bias = False
67
+ if hasattr(old_module, 'bias') and old_module.bias is not None:
68
+ need_bias = True
69
+
70
+ # Initialize the new module
71
+ if isinstance(old_module, LinearLayer_Compress) or isinstance(old_module, torch.nn.Linear):
72
+ if isinstance(old_module, LinearLayer_Compress):
73
+ new_module = old_module
74
+ else:
75
+ new_module = LinearLayer_Compress(old_module.in_features, old_module.out_features,
76
+ bias=need_bias).to(device=old_module.weight.device,
77
+ dtype=old_module.weight.dtype)
78
+ new_module.weight.data = old_module.weight.data
79
+ if need_bias:
80
+ new_module.bias.data = old_module.bias.data
81
+ elif isinstance(old_module, Conv2dLayer_Compress) or isinstance(old_module, torch.nn.Conv2d):
82
+ if isinstance(old_module, Conv2dLayer_Compress):
83
+ new_module = old_module
84
+ else:
85
+ new_module = Conv2dLayer_Compress(old_module.in_channels, old_module.out_channels, old_module.kernel_size, old_module.stride, old_module.padding, \
86
+ old_module.dilation, old_module.groups, need_bias, \
87
+ old_module.padding_mode).to(device=old_module.weight.device, dtype=old_module.weight.dtype)
88
+ new_module.weight.data = old_module.weight.data
89
+ if need_bias:
90
+ new_module.bias.data = old_module.bias.data
91
+ elif isinstance(old_module, torch.nn.BatchNorm2d):
92
+ new_module = BNLayer_Compress(old_module.num_features, old_module.eps, old_module.momentum, old_module.affine,
93
+ old_module.track_running_stats).to(old_module.weight.device,
94
+ old_module.weight.dtype)
95
+ new_module.weight.data = old_module.weight.data
96
+ if need_bias:
97
+ new_module.bias.data = old_module.bias.data
98
+ new_module.running_mean.data = old_module.running_mean.data
99
+ new_module.running_var.data = old_module.running_var.data
100
+ elif isinstance(old_module, Embedding_Compress) or isinstance(old_module, torch.nn.Embedding):
101
+ if isinstance(old_module, Embedding_Compress):
102
+ new_module = old_module
103
+ else:
104
+ new_module = Embedding_Compress(old_module.num_embeddings, old_module.embedding_dim, old_module.padding_idx, old_module.max_norm, old_module.norm_type, \
105
+ old_module.scale_grad_by_freq, old_module.sparse).to(device=old_module.weight.device, dtype=old_module.weight.dtype)
106
+ new_module.weight.data = old_module.weight.data
107
+ elif mpu is not None and (isinstance(old_module, ColumnParallelLinear_Compress)
108
+ or isinstance(old_module, mpu.ColumnParallelLinear)):
109
+ if isinstance(old_module, ColumnParallelLinear_Compress):
110
+ new_module = old_module
111
+ else:
112
+ new_module = ColumnParallelLinear_Compress(mpu,
113
+ old_module.input_size,
114
+ old_module.output_size,
115
+ gather_output=old_module.gather_output,
116
+ skip_bias_add=old_module.skip_bias_add,
117
+ bias=need_bias).to(device=old_module.weight.device,
118
+ dtype=old_module.weight.dtype)
119
+ new_module.weight.data = old_module.weight.data
120
+ if need_bias:
121
+ new_module.bias.data = old_module.bias.data
122
+ elif mpu is not None and (isinstance(old_module, RowParallelLinear_Compress)
123
+ or isinstance(old_module, mpu.RowParallelLinear)):
124
+ if isinstance(old_module, RowParallelLinear_Compress):
125
+ new_module = old_module
126
+ else:
127
+ new_module = RowParallelLinear_Compress(mpu,
128
+ old_module.input_size,
129
+ old_module.output_size,
130
+ input_is_parallel=old_module.input_is_parallel,
131
+ skip_bias_add=old_module.skip_bias_add,
132
+ bias=need_bias).to(device=old_module.weight.device,
133
+ dtype=old_module.weight.dtype)
134
+ new_module.weight.data = old_module.weight.data
135
+ if need_bias:
136
+ new_module.bias.data = old_module.bias.data
137
+ else:
138
+ new_module = None
139
+
140
+ if compression_technique is not None:
141
+ for k, v in compression_technique.items():
142
+ if k == SPARSE_PRUNING:
143
+ if v[SPARSE_PRUNING_ENABLED]:
144
+ new_module.enable_sparse_pruning(v[SPARSE_PRUNING_DENSE_RATIO], v[SPARSE_PRUNING_METHOD])
145
+ elif k == ROW_PRUNING:
146
+ if v[ROW_PRUNING_ENABLED]:
147
+ new_module.enable_row_pruning(v[ROW_PRUNING_DENSE_RATIO], v[ROW_PRUNING_METHOD])
148
+ elif k == HEAD_PRUNING:
149
+ if v[HEAD_PRUNING_ENABLED]:
150
+ new_module.enable_head_pruning(v[HEAD_PRUNING_DENSE_RATIO], v[HEAD_PRUNING_METHOD],
151
+ v[HEAD_PRUNING_NUM_HEADS])
152
+ elif k == ACTIVATION_QUANTIZATION:
153
+ if v[ACTIVATION_QUANTIZATION_ENABLED]:
154
+ new_module.enable_activation_quantization(v[ACTIVATION_QUANTIZE_BITS], v[ACTIVATION_QUANTIZE_TYPE],
155
+ v[ACTIVATION_QUANTIZE_RANGE])
156
+ elif k == WEIGHT_QUANTIZATION:
157
+ if v[WEIGHT_QUANTIZE_ENABLED]:
158
+ new_module.enable_weight_quantization(v[WEIGHT_QUANTIZE_START_BITS],
159
+ v[WEIGHT_QUANTIZE_TARGET_BITS],
160
+ v[WEIGHT_QUANTIZATION_PERIOD],
161
+ v[WEIGHT_QUANTIZE_IN_FORWARD_ENABLED],
162
+ v[WEIGHT_QUANTIZE_TYPE], v[WEIGHT_QUANTIZE_GROUPS])
163
+ elif k == CHANNEL_PRUNING:
164
+ if v[CHANNEL_PRUNING_ENABLED]:
165
+ new_module.enable_channel_pruning(v[CHANNEL_PRUNING_DENSE_RATIO], v[CHANNEL_PRUNING_METHOD])
166
+ else:
167
+ raise NotImplementedError('Compression technique {} is not implemented'.format(k))
168
+
169
+ # Replace the old module with the new one
170
+ recursive_setattr(model, module_name, new_module)
171
+
172
+
173
+ def is_module_compressible(module, mpu=None):
174
+ ret = isinstance(module, torch.nn.Linear) or \
175
+ isinstance(module, torch.nn.Conv2d) or \
176
+ isinstance(module, torch.nn.Embedding) or \
177
+ isinstance(module, torch.nn.BatchNorm2d)
178
+
179
+ if mpu is not None:
180
+ ret = ret or isinstance(module, mpu.RowParallelLinear) or isinstance(module, mpu.ColumnParallelLinear)
181
+
182
+ return ret
183
+
184
+
185
+ def compression_preparation(model, compression_technique_list, mpu):
186
+ """
187
+ Prepare the compression techniques of a model.
188
+ Args:
189
+ model (`torch.nn.Module`)
190
+ The model to prepare the compression techniques of.
191
+ compression_technique_list (`list`)
192
+ The list of compression techniques to prepare the model to.
193
+ list[]
194
+ """
195
+ # Here we first replace all module with our linear wrapper
196
+ for module_name, module in model.named_modules():
197
+ if is_module_compressible(module, mpu):
198
+ module_replacement(model, module_name, mpu=mpu)
199
+ for module_name_lists, _, compression_technique in compression_technique_list:
200
+ for mnl in module_name_lists:
201
+ for module_name in mnl:
202
+ module_replacement(model, module_name, compression_technique)
203
+
204
+ return model
205
+
206
+
207
+ def fix_compression(model, module_name, compression_technique, mask=None, dim_reduction=False):
208
+ """
209
+ Fix the compression technique of a module.
210
+ Args:
211
+ model (`torch.nn.Module`)
212
+ The model to fix the compression technique of.
213
+ module_name (`str`)
214
+ The name of the module to fix the compression technique of.
215
+ compression_technique (`str`)
216
+ The compression technique to fix the module to.
217
+ """
218
+ # Here we can make things much simpler by just replacing the module
219
+ module = recursive_getattr(model, module_name)
220
+ for k, v in compression_technique.items():
221
+ if k == WEIGHT_QUANTIZATION and v[WEIGHT_QUANTIZE_IN_FORWARD_ENABLED] and v[WEIGHT_QUANTIZE_ENABLED]:
222
+ return module.fix_weight_quantization()
223
+ elif k == SPARSE_PRUNING and v[SPARSE_PRUNING_ENABLED]:
224
+ return module.fix_sparse_pruning_helper()
225
+ elif k == ROW_PRUNING and (v[ROW_PRUNING_ENABLED] or mask is not None):
226
+ return module.fix_row_col_pruning_helper(mask, dim_reduction=dim_reduction)
227
+ elif k == HEAD_PRUNING and (v[HEAD_PRUNING_ENABLED] or mask is not None):
228
+ return module.fix_head_pruning_helper(mask, v[HEAD_PRUNING_NUM_HEADS], dim_reduction=dim_reduction)
229
+ elif k == CHANNEL_PRUNING and (v[CHANNEL_PRUNING_ENABLED] or mask is not None):
230
+ return module.fix_channel_pruning_helper(mask, dim_reduction=dim_reduction)
231
+
232
+
233
+ def convert_conv1d_to_linear(model, convert_type):
234
+ '''
235
+ This is a help function to convert conv1d to linear (e.g., convert GPT2 from HF)
236
+ '''
237
+ if hasattr(model, 'module'):
238
+ c_model = model.module
239
+ else:
240
+ c_model = model
241
+
242
+ for name, module in c_model.named_modules():
243
+ if isinstance(module, convert_type):
244
+ old_module = recursive_getattr(c_model, name)
245
+ new_module = torch.nn.Linear(old_module.weight.data.size(0),
246
+ old_module.weight.data.size(1),
247
+ bias=True if old_module.bias is not None else False)
248
+ new_module.weight.data = old_module.weight.data.t().contiguous()
249
+ if new_module.bias is not None:
250
+ new_module.bias.data = old_module.bias.data.view(-1)
251
+
252
+ recursive_setattr(c_model, name, new_module)
253
+
254
+ return model
255
+
256
+
257
+ def generate_pruners(config, model):
258
+ """Generate pruners.
259
+ Args:
260
+ config (`neural_compressor.WeightPruningConfig`)
261
+ The object to the class WeightPruningConfig.
262
+ model (`torch.nn.module`)
263
+ The torch module object to be pruned.
264
+ """
265
+ assert nc_pruner is not None, "please ensure the neural_compressor python package is installed by pip or conda if user wants to use snip_momentum sparse pruning"
266
+ from nc_pruner.utils import process_config, parse_to_prune
267
+ from nc_pruner.pruners import get_pruner
268
+ assert isinstance(model, torch.nn.Module)
269
+ pruners_info = process_config(config)
270
+ pruners = []
271
+ for info in pruners_info:
272
+ modules = parse_to_prune(info, model)
273
+ if modules == {}:
274
+ logger.warning("one pruner hooks no layers, please have a check")
275
+
276
+ pruners.append(get_pruner(info, modules))
277
+ info['modules'] = [key for key in modules.keys()]
278
+ info['len_of_modules'] = len(info['modules'])
279
+ logger.info(info)
280
+ return pruners
281
+
282
+
283
+ def register_on_step_begin(model):
284
+ """Mount on_step_begin to the model.
285
+ Args:
286
+ model (`torch.nn.module`)
287
+ The torch module object to be pruned.
288
+ """
289
+
290
+ def hook(module, input):
291
+ for pruner in module.pruners:
292
+ pruner.on_step_begin(0)
293
+
294
+ hook_handle = model.register_forward_pre_hook(hook)
295
+ return hook_handle
296
+
297
+
298
+ def rewrite_optimizer_step(opt: torch.optim.Optimizer):
299
+ """Mount on_before/after_optimizer_step to the optimizer.
300
+ Args:
301
+ model (`torch.opt.Optimizer`)
302
+ The torch optimizer object to be hooked.
303
+ """
304
+
305
+ def new_step(self, closure=None):
306
+ if hasattr(self, "pruners"):
307
+ for pruner in self.pruners:
308
+ pruner.on_before_optimizer_step()
309
+
310
+ if closure is not None:
311
+ res = self.orig_step(closure)
312
+ else:
313
+ res = self.orig_step()
314
+ if hasattr(self, "pruners"):
315
+ for pruner in self.pruners:
316
+ pruner.on_after_optimizer_step()
317
+ return res
318
+
319
+ opt.orig_step = opt.step
320
+ import types
321
+ opt.step = types.MethodType(new_step, opt)
322
+ return opt
lib/python3.12/site-packages/deepspeed/compression/scheduler.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ from .compress import get_module_name
7
+ from .constants import *
8
+ from .helper import recursive_getattr
9
+ from deepspeed.utils import logger
10
+
11
+
12
+ class compression_scheduler():
13
+ '''
14
+ Used to schedule different compression methods
15
+ '''
16
+
17
+ def __init__(self, model, compression_config):
18
+ self.model = model
19
+ self.compression_config = compression_config
20
+ self.make_init()
21
+ self.training_steps = 0
22
+ self.weight_quantization_enabled = False
23
+
24
+ self.verbose = {
25
+ WEIGHT_QUANTIZATION: False,
26
+ ACTIVATION_QUANTIZATION: False,
27
+ SPARSE_PRUNING: False,
28
+ HEAD_PRUNING: False,
29
+ ROW_PRUNING: False,
30
+ CHANNEL_PRUNING: False
31
+ }
32
+
33
+ def make_init(self):
34
+ self.different_compression_methods = {}
35
+ for method, method_content in self.compression_config.items():
36
+ if LAYER_REDUCTION in method:
37
+ continue
38
+ self.different_compression_methods[method] = {
39
+ TECHNIQUE_ENABLED: False,
40
+ SHARED_PARAMETERS: None,
41
+ DIFFERENT_GROUPS: []
42
+ }
43
+ exist_module_name = set()
44
+ shared_parameters = method_content[SHARED_PARAMETERS]
45
+ self.different_compression_methods[method][TECHNIQUE_ENABLED] = shared_parameters[TECHNIQUE_ENABLED]
46
+ self.different_compression_methods[method][SHARED_PARAMETERS] = shared_parameters
47
+
48
+ for group_name, method_parameters in method_content[DIFFERENT_GROUPS].items():
49
+ module_name_list = []
50
+ for key_word in method_parameters[DIFFERENT_GROUPS_MODULE_SCOPE]:
51
+ module_name, exist_module_name = get_module_name(group_name,
52
+ self.model,
53
+ key_word,
54
+ exist_module_name,
55
+ verbose=False)
56
+ module_name_list.extend(module_name)
57
+ if module_name_list:
58
+ self.different_compression_methods[method][DIFFERENT_GROUPS].append(
59
+ [group_name, module_name_list,
60
+ method_parameters.copy().pop('params')])
61
+
62
+ def check_weight_quantization(self):
63
+ # check weight quantization
64
+ wq = self.different_compression_methods[WEIGHT_QUANTIZATION]
65
+ if not wq[TECHNIQUE_ENABLED]:
66
+ return
67
+ else:
68
+ shared_parameters = wq[SHARED_PARAMETERS]
69
+ if self.training_steps >= shared_parameters[TECHNIQUE_SCHEDULE_OFFSET]:
70
+ for group_name, module_name_list, method_parameters in wq[DIFFERENT_GROUPS]:
71
+ for module_name in module_name_list:
72
+ module = recursive_getattr(self.model, module_name)
73
+ module.weight_quantization_enabled = True
74
+
75
+ if not self.verbose[WEIGHT_QUANTIZATION]:
76
+ logger.info(f'Weight quantization is enabled at step {self.training_steps}')
77
+ self.weight_quantization_enabled = True
78
+ self.verbose[WEIGHT_QUANTIZATION] = True
79
+
80
+ def check_activation_quantization(self):
81
+ # check activation quantization
82
+ aq = self.different_compression_methods[ACTIVATION_QUANTIZATION]
83
+ if not aq[TECHNIQUE_ENABLED]:
84
+ return
85
+ else:
86
+ shared_parameters = aq[SHARED_PARAMETERS]
87
+ if self.training_steps >= shared_parameters[TECHNIQUE_SCHEDULE_OFFSET]:
88
+ for group_name, module_name_list, method_parameters in aq[DIFFERENT_GROUPS]:
89
+ for module_name in module_name_list:
90
+ module = recursive_getattr(self.model, module_name)
91
+ module.activation_quantization_enabled = True
92
+ if not self.verbose[ACTIVATION_QUANTIZATION]:
93
+ logger.info(f'Activation quantization is enabled at step {self.training_steps}')
94
+ self.verbose[ACTIVATION_QUANTIZATION] = True
95
+
96
+ def check_sparse_pruning(self):
97
+ # check sparse pruning
98
+ sp = self.different_compression_methods[SPARSE_PRUNING]
99
+ if not sp[TECHNIQUE_ENABLED]:
100
+ return
101
+ else:
102
+ shared_parameters = sp[SHARED_PARAMETERS]
103
+ if shared_parameters[TECHNIQUE_SCHEDULE_OFFSET] <= self.training_steps <= shared_parameters[
104
+ TECHNIQUE_SCHEDULE_OFFSET_END]:
105
+ for group_name, module_name_list, method_parameters in sp[DIFFERENT_GROUPS]:
106
+ for module_name in module_name_list:
107
+ module = recursive_getattr(self.model, module_name)
108
+ module.sparse_pruning_enabled = True
109
+ if not self.verbose[SPARSE_PRUNING]:
110
+ logger.info(f'Sparse pruning is enabled at step {self.training_steps}')
111
+ self.verbose[SPARSE_PRUNING] = True
112
+
113
+ def check_head_pruning(self):
114
+ # check head pruning
115
+ hp = self.different_compression_methods[HEAD_PRUNING]
116
+ if not hp[TECHNIQUE_ENABLED]:
117
+ return
118
+ else:
119
+ shared_parameters = hp[SHARED_PARAMETERS]
120
+ if self.training_steps >= shared_parameters[TECHNIQUE_SCHEDULE_OFFSET]:
121
+ for group_name, module_name_list, method_parameters in hp[DIFFERENT_GROUPS]:
122
+ for module_name in module_name_list:
123
+ module = recursive_getattr(self.model, module_name)
124
+ module.head_pruning_enabled = True
125
+ if not self.verbose[HEAD_PRUNING]:
126
+ logger.info(f'Head pruning is enabled at step {self.training_steps}')
127
+ self.verbose[HEAD_PRUNING] = True
128
+
129
+ def check_row_pruning(self):
130
+ # check row pruning
131
+ rp = self.different_compression_methods[ROW_PRUNING]
132
+ if not rp[TECHNIQUE_ENABLED]:
133
+ return
134
+ else:
135
+ shared_parameters = rp[SHARED_PARAMETERS]
136
+ if self.training_steps >= shared_parameters[TECHNIQUE_SCHEDULE_OFFSET]:
137
+ for group_name, module_name_list, method_parameters in rp[DIFFERENT_GROUPS]:
138
+ for module_name in module_name_list:
139
+ module = recursive_getattr(self.model, module_name)
140
+ module.row_pruning_enabled = True
141
+ if not self.verbose[ROW_PRUNING]:
142
+ logger.info(f'Row pruning is enabled at step {self.training_steps}')
143
+ self.verbose[ROW_PRUNING] = True
144
+
145
+ def check_channel_pruning(self):
146
+ # check channel pruning
147
+ cp = self.different_compression_methods[CHANNEL_PRUNING]
148
+ if not cp[TECHNIQUE_ENABLED]:
149
+ return
150
+ else:
151
+ shared_parameters = cp[SHARED_PARAMETERS]
152
+ if self.training_steps >= shared_parameters[TECHNIQUE_SCHEDULE_OFFSET]:
153
+ for group_name, module_name_list, method_parameters in cp[DIFFERENT_GROUPS]:
154
+ for module_name in module_name_list:
155
+ module = recursive_getattr(self.model, module_name)
156
+ module.channel_pruning_enabled = True
157
+ if not self.verbose[CHANNEL_PRUNING]:
158
+ logger.info(f'Channel pruning is enabled at step {self.training_steps}')
159
+ self.verbose[CHANNEL_PRUNING] = True
160
+
161
+ def check_all_modules(self):
162
+ # check all different compression methods we have
163
+ self.check_weight_quantization()
164
+ self.check_activation_quantization()
165
+ self.check_sparse_pruning()
166
+ self.check_head_pruning()
167
+ self.check_row_pruning()
168
+ self.check_channel_pruning()
169
+
170
+ def step(self, step_zero_check=False):
171
+ if not step_zero_check:
172
+ self.training_steps += 1
173
+ self.check_all_modules()
lib/python3.12/site-packages/deepspeed/compression/utils.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ import torch
7
+ from torch import autograd
8
+ import math
9
+
10
+
11
+ class TopKBinarizer(autograd.Function):
12
+ """
13
+ Top-k Binarizer.
14
+ Computes a binary mask M from a real value matrix S such that `M_{i,j} = 1` if and only if `S_{i,j}`
15
+ is among the k% highest values of S.
16
+ Implementation is inspired from:
17
+ https://github.com/yaozhewei/MLPruning
18
+ """
19
+
20
+ @staticmethod
21
+ def forward(ctx, inputs: torch.tensor, threshold: float, sigmoid: bool):
22
+ """
23
+ Args:
24
+ inputs (`torch.FloatTensor`)
25
+ The input matrix from which the binarizer computes the binary mask.
26
+ threshold (`float`)
27
+ The percentage of weights to keep (the rest is pruned).
28
+ `threshold` is a float between 0 and 1.
29
+ sigmoid (`bool`)
30
+ Whether to apply a sigmoid on the threshold
31
+ Returns:
32
+ mask (`torch.FloatTensor`)
33
+ Binary matrix of the same size as `inputs` acting as a mask (1 - the associated weight is
34
+ retained, 0 - the associated weight is pruned).
35
+ """
36
+ # Get the subnetwork by sorting the inputs and using the top threshold
37
+ if sigmoid:
38
+ threshold = torch.sigmoid(threshold).item()
39
+ ctx.sigmoid = sigmoid
40
+ mask = inputs.clone()
41
+
42
+ _, idx = inputs.flatten().sort(descending=True)
43
+ j = math.ceil(threshold * inputs.numel())
44
+
45
+ # flat_out and mask access the same memory.
46
+ flat_out = mask.flatten()
47
+ flat_out[idx[j:]] = 0.
48
+ flat_out[idx[:j]] = 1.
49
+ ctx.save_for_backward(mask)
50
+
51
+ return mask
52
+
53
+ @staticmethod
54
+ def backward(ctx, gradOutput):
55
+ mask, = ctx.saved_tensors
56
+ if ctx.sigmoid:
57
+ return gradOutput.clone(), ((gradOutput * mask).sum()).view(-1), None
58
+ else:
59
+ return gradOutput.clone(), None, None
60
+
61
+
62
+ class SymQuantizer(torch.autograd.Function):
63
+ """
64
+ Symmetric quantization
65
+ """
66
+
67
+ @staticmethod
68
+ def forward(ctx, input, num_bits, min_value=None, max_value=None, num_groups=1):
69
+ """
70
+ Args:
71
+ inputs (`torch.FloatTensor`)
72
+ The input which needs to be quantized
73
+ num_bits (int, >=4)
74
+ Number of bits to use for quantization
75
+ min_value/max_value (torch.FloatTensor)
76
+ Used for static activation quantization
77
+ num_groups (int)
78
+ How many groups to partition the quantization into
79
+ Returns:
80
+ quantized_input (`torch.FloatTensor`)
81
+ Quantized input
82
+ """
83
+ assert (min_value is None and max_value is None) or (min_value is not None and max_value is not None
84
+ and num_groups == 1)
85
+ q_range = 2**num_bits
86
+ input_shape = input.shape
87
+ if min_value is None:
88
+ input = input.reshape(num_groups, -1)
89
+ max_input = torch.amax(torch.abs(input), dim=-1).view(num_groups, -1)
90
+ else:
91
+ max_input = torch.max(min_value.abs(), max_value).view(-1)
92
+
93
+ scale = 2 * max_input / q_range
94
+ output = (input / scale).round().clamp(-q_range // 2, q_range // 2 - 1) * scale
95
+ output = output.reshape(input_shape).contiguous()
96
+ return output
97
+
98
+ @staticmethod
99
+ def backward(ctx, grad_output):
100
+ grad_input = grad_output.clone()
101
+ return grad_input, None, None, None, None
102
+
103
+
104
+ class AsymQuantizer(torch.autograd.Function):
105
+ """
106
+ Asymmetric quantization
107
+ """
108
+
109
+ @staticmethod
110
+ def forward(ctx, input, num_bits, min_value=None, max_value=None, num_groups=1):
111
+ """
112
+ Args:
113
+ inputs (`torch.FloatTensor`)
114
+ The input which needs to be quantized
115
+ num_bits (int, >=4)
116
+ Number of bits to use for quantization
117
+ min_value/max_value (torch.FloatTensor)
118
+ Used for static activation quantization
119
+ num_groups (int)
120
+ How many groups to partition the quantization into
121
+ Returns:
122
+ quantized_input (`torch.FloatTensor`)
123
+ Quantized input
124
+ """
125
+
126
+ assert (min_value is None and max_value is None) or (min_value is not None and max_value is not None
127
+ and num_groups == 1)
128
+ q_range = 2**num_bits
129
+ input_shape = input.shape
130
+ if min_value is None:
131
+ input = input.reshape(num_groups, -1)
132
+ min_value = input.amin(dim=-1, keepdim=True)
133
+ max_value = input.amax(dim=-1, keepdim=True)
134
+
135
+ scale = (max_value - min_value) / q_range
136
+ zero_point = (min_value / scale).round() * scale
137
+
138
+ output = ((input - zero_point) / scale).round().clamp(0, q_range - 1) * scale + zero_point
139
+ output = output.reshape(input_shape).contiguous()
140
+ return output
141
+
142
+ @staticmethod
143
+ def backward(ctx, grad_output):
144
+ grad_input = grad_output.clone()
145
+ return grad_input, None, None, None, None
146
+
147
+
148
+ class TernaryQuantizer(torch.autograd.Function):
149
+ """
150
+ Ternary quantization
151
+ """
152
+
153
+ @staticmethod
154
+ def forward(ctx, input, num_bits, min_value=None, max_value=None, num_groups=1):
155
+ """
156
+ Args:
157
+ inputs (`torch.FloatTensor`)
158
+ The input which needs to be quantized
159
+ num_bits (int)
160
+ Dummy variable
161
+ min_value/max_value (torch.FloatTensor)
162
+ Used for static activation quantization; for now they are dummy variable
163
+ num_groups (int)
164
+ How many groups to partition the quantization into
165
+ Returns:
166
+ quantized_input (`torch.FloatTensor`)
167
+ Quantized input
168
+ """
169
+
170
+ assert (min_value is None and max_value is None)
171
+ input_flat = input.reshape(num_groups, -1)
172
+ n = input_flat.shape[1]
173
+ m = input_flat.norm(p=1, dim=1).div(n)
174
+ thres = (0.7 * m).view(-1, 1)
175
+ pos = (input_flat > thres).type(input.type())
176
+ neg = (input_flat < -thres).type(input.type())
177
+ mask = (input_flat.abs() > thres).type(input.type())
178
+ alpha = ((mask * input_flat).abs().sum(dim=1) / mask.sum(dim=1)).view(-1, 1)
179
+ output = alpha * pos - alpha * neg
180
+ output = output.reshape(input.shape).contiguous()
181
+ return output
182
+
183
+ @staticmethod
184
+ def backward(ctx, grad_output):
185
+ grad_input = grad_output.clone()
186
+ return grad_input, None, None, None, None
187
+
188
+
189
+ class BinaryQuantizer(torch.autograd.Function):
190
+ """
191
+ Binary quantization
192
+ """
193
+
194
+ @staticmethod
195
+ def forward(ctx, input, num_bits, min_value=None, max_value=None, num_groups=1):
196
+ """
197
+ Args:
198
+ inputs (`torch.FloatTensor`)
199
+ The input which needs to be quantized
200
+ num_bits (int)
201
+ Dummy variable
202
+ min_value/max_value (torch.FloatTensor)
203
+ Used for static activation quantization; for now they are dummy variable
204
+ num_groups (int)
205
+ How many groups to partition the quantization into
206
+ Returns:
207
+ quantized_input (`torch.FloatTensor`)
208
+ Quantized input
209
+ """
210
+
211
+ assert (min_value is None and max_value is None)
212
+ input_flat = input.reshape(num_groups, -1)
213
+ n = input_flat.shape[1]
214
+ m = input_flat.norm(p=1, dim=1, keepdim=True).div(n)
215
+ output = input_flat.sign().mul(m)
216
+ output = output.reshape(input.shape).contiguous()
217
+ return output
218
+
219
+ @staticmethod
220
+ def backward(ctx, grad_output):
221
+ grad_input = grad_output.clone()
222
+ return grad_input, None, None, None, None
lib/python3.12/site-packages/deepspeed/ops/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (591 Bytes). View file
 
lib/python3.12/site-packages/deepspeed/ops/compile/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # DeepSpeed Team
5
+
6
+ from ..op_builder import DeepCompileBuilder
lib/python3.12/site-packages/deepspeed/ops/compile/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (258 Bytes). View file
 
lib/python3.12/site-packages/deepspeed/ops/csrc/adagrad/cpu_adagrad.cpp ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include "cpu_adagrad.h"
7
+ #include <torch/extension.h>
8
+ #include <functional>
9
+ #include <iostream>
10
+ #include <map>
11
+ #include <memory>
12
+ #include <type_traits>
13
+ #include <unordered_map>
14
+
15
+ using namespace std::string_literals;
16
+ static std::unordered_map<int, std::shared_ptr<void>> s_optimizers;
17
+
18
+ // C++ interface
19
+
20
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
21
+ void Adagrad_Optimizer::Step_1(ds_params_precision_t* _params,
22
+ ds_params_precision_t* grads,
23
+ ds_state_precision_t* _exp_avg_sq,
24
+ size_t _param_size)
25
+ {
26
+ size_t rounded_size = 0;
27
+ #if defined(__AVX512__) or defined(__AVX256__)
28
+ Step_AVX<1>(&rounded_size, _params, grads, _exp_avg_sq, _param_size);
29
+ #endif
30
+ if (_param_size > rounded_size) {
31
+ float step_size = -1 * _alpha;
32
+ for (size_t t = rounded_size; t < _param_size; t += TILE) {
33
+ size_t copy_size = TILE;
34
+ if ((t + TILE) > _param_size) copy_size = _param_size - t;
35
+ size_t offset = copy_size + t;
36
+ #pragma omp parallel for
37
+ for (size_t k = t; k < offset; k++) {
38
+ float grad = (float)grads[k];
39
+ float param = (float)_params[k];
40
+ float momentum = grads[k];
41
+ float variance = _exp_avg_sq[k];
42
+ if (_weight_decay > 0) { grad = param * _weight_decay + grad; }
43
+
44
+ variance += grad * grad;
45
+
46
+ grad = sqrt(variance);
47
+ grad += _eps;
48
+ grad = momentum / grad;
49
+ param = grad * step_size + param;
50
+ _params[k] = param;
51
+ // STORE UPDATE TERM TO GRAD'S MEMORY
52
+ grads[k] = grad * step_size;
53
+ _exp_avg_sq[k] = variance;
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
60
+ void Adagrad_Optimizer::Step_4(ds_params_precision_t* _params,
61
+ ds_params_precision_t* grads,
62
+ ds_state_precision_t* _exp_avg_sq,
63
+ size_t _param_size)
64
+ {
65
+ size_t rounded_size = 0;
66
+ #if defined(__AVX512__) or defined(__AVX256__)
67
+ Step_AVX<4>(&rounded_size, _params, grads, _exp_avg_sq, _param_size);
68
+ #endif
69
+ if (_param_size > rounded_size)
70
+ Step_1((_params + rounded_size),
71
+ (grads + rounded_size),
72
+ (_exp_avg_sq + rounded_size),
73
+ (_param_size - rounded_size));
74
+ }
75
+
76
+ int create_adagrad_optimizer(int optimizer_id,
77
+ float alpha = 1e-2,
78
+ float eps = 1e-8,
79
+ float weight_decay = 0,
80
+ bool should_log = false)
81
+ {
82
+ auto opt = std::make_shared<Adagrad_Optimizer>(alpha, eps, weight_decay);
83
+
84
+ s_optimizers[optimizer_id] = opt;
85
+
86
+ if (should_log) {
87
+ std::string avx_type = "";
88
+ #if defined(__AVX512__)
89
+ avx_type = "AVX512";
90
+ #else
91
+ #if defined(__AVX256__)
92
+ avx_type = "AVX2";
93
+ #else
94
+ avx_type = "scalar";
95
+ #endif
96
+ #endif
97
+
98
+ printf("Adagrad Optimizer #%d is created with %s arithmetic capability.\n",
99
+ optimizer_id,
100
+ avx_type.c_str());
101
+ printf("Config: alpha=%f, weight_decay=%f\n", alpha, weight_decay);
102
+ }
103
+
104
+ return 0;
105
+ }
106
+
107
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
108
+ void Adagrad_Optimizer::Step_8(ds_params_precision_t* _params,
109
+ ds_params_precision_t* grads,
110
+ ds_state_precision_t* _exp_avg_sq,
111
+ size_t _param_size)
112
+ {
113
+ size_t rounded_size = 0;
114
+ #if defined(__AVX512__) or defined(__AVX256__)
115
+ Step_AVX<8>(&rounded_size, _params, grads, _exp_avg_sq, _param_size);
116
+ #endif
117
+ if (_param_size > rounded_size)
118
+ Step_4((_params + rounded_size),
119
+ (grads + rounded_size),
120
+ (_exp_avg_sq + rounded_size),
121
+ (_param_size - rounded_size));
122
+ }
123
+
124
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
125
+ void step_invoker(std::shared_ptr<Adagrad_Optimizer> opt,
126
+ void* _params,
127
+ void* grads,
128
+ void* _exp_avg_sq,
129
+ size_t _param_size)
130
+ {
131
+ opt->Step_8((ds_params_precision_t*)(_params),
132
+ (ds_params_precision_t*)(grads),
133
+ (ds_state_precision_t*)(_exp_avg_sq),
134
+ _param_size);
135
+ }
136
+
137
+ std::map<std::tuple<c10::ScalarType, c10::ScalarType>,
138
+ std::function<void(std::shared_ptr<Adagrad_Optimizer>, void*, void*, void*, size_t)>>
139
+ invokers;
140
+
141
+ // Fill map with template functions for each type
142
+ template <class ds_params_precision_t, class ds_state_precision_t>
143
+ void create_invoker()
144
+ {
145
+ invokers[std::tuple(c10::CppTypeToScalarType<ds_params_precision_t>(),
146
+ c10::CppTypeToScalarType<ds_state_precision_t>())] =
147
+ step_invoker<ds_params_precision_t, ds_state_precision_t>;
148
+ }
149
+ struct InvokerInitializer {
150
+ InvokerInitializer()
151
+ {
152
+ create_invoker<c10::Half, float>();
153
+ create_invoker<c10::Half, c10::Half>();
154
+ create_invoker<c10::BFloat16, float>();
155
+ create_invoker<c10::BFloat16, c10::BFloat16>();
156
+ create_invoker<float, float>();
157
+ }
158
+ } _invoker_initializer;
159
+
160
+ void invoke(std::shared_ptr<Adagrad_Optimizer> opt,
161
+ torch::Tensor& params,
162
+ torch::Tensor& grads,
163
+ torch::Tensor& exp_avg_sq,
164
+ size_t param_size)
165
+ {
166
+ c10::ScalarType params_type = at::typeMetaToScalarType(params.options().dtype());
167
+ c10::ScalarType state_type = at::typeMetaToScalarType(exp_avg_sq.options().dtype());
168
+
169
+ auto it = invokers.find(std::tuple(params_type, state_type));
170
+ if (it == invokers.end()) {
171
+ throw std::runtime_error("Adagrad optimizer with param type "s +
172
+ c10::toString(params_type) + " and state type "s +
173
+ c10::toString(state_type) +
174
+ " is not supported on current hardware"s);
175
+ }
176
+
177
+ it->second(opt, params.data_ptr(), grads.data_ptr(), exp_avg_sq.data_ptr(), param_size);
178
+ }
179
+
180
+ int ds_adagrad_step(int optimizer_id,
181
+ size_t step,
182
+ float lr,
183
+ float epsilon,
184
+ float weight_decay,
185
+ torch::Tensor& params,
186
+ torch::Tensor& grads,
187
+ torch::Tensor& exp_avg_sq)
188
+ {
189
+ auto params_c = params.contiguous();
190
+ auto grads_c = grads.contiguous();
191
+ auto exp_avg_sq_c = exp_avg_sq.contiguous();
192
+
193
+ std::shared_ptr<Adagrad_Optimizer> opt =
194
+ std::static_pointer_cast<Adagrad_Optimizer>(s_optimizers[optimizer_id]);
195
+ opt->IncrementStep(step);
196
+ opt->update_state(lr, epsilon, weight_decay);
197
+
198
+ invoke(opt, params_c, grads_c, exp_avg_sq_c, params_c.numel());
199
+
200
+ return 0;
201
+ }
202
+
203
+ int destroy_adagrad_optimizer(int optimizer_id)
204
+ {
205
+ s_optimizers.erase(optimizer_id);
206
+
207
+ return 0;
208
+ }
209
+
210
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
211
+ {
212
+ m.def("adagrad_update", &ds_adagrad_step, "DeepSpeed CPU Adagrad update (C++)");
213
+ m.def("create_adagrad", &create_adagrad_optimizer, "DeepSpeed CPU Adagrad (C++)");
214
+ m.def("destroy_adagrad", &destroy_adagrad_optimizer, "DeepSpeed CPU Adagrad destroy (C++)");
215
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam.cpp ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include "cpu_adam.h"
7
+
8
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
9
+ {
10
+ m.def("adam_update", &ds_adam_step, "DeepSpeed CPU Adam update (C++)");
11
+ m.def("create_adam", &create_adam_optimizer, "DeepSpeed CPU Adam (C++)");
12
+ m.def("destroy_adam", &destroy_adam_optimizer, "DeepSpeed CPU Adam destroy (C++)");
13
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam_impl.cpp ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include <torch/extension.h>
7
+ #include <cassert>
8
+ #include <functional>
9
+ #include <iostream>
10
+ #include <map>
11
+ #include <memory>
12
+ #include <type_traits>
13
+ #include <unordered_map>
14
+ #include "cpu_adam.h"
15
+
16
+ using namespace std::string_literals;
17
+ static std::unordered_map<int, std::shared_ptr<void>> s_optimizers;
18
+
19
+ // C++ interface
20
+
21
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
22
+ void Adam_Optimizer::Step_1(ds_params_precision_t* _params,
23
+ ds_params_precision_t* grads,
24
+ ds_state_precision_t* _exp_avg,
25
+ ds_state_precision_t* _exp_avg_sq,
26
+ size_t _param_size)
27
+ {
28
+ size_t rounded_size = 0;
29
+ #if defined(__AVX512__) or defined(__AVX256__)
30
+ Step_AVX<1>(&rounded_size, _params, grads, _exp_avg, _exp_avg_sq, _param_size);
31
+ #endif
32
+ if (_param_size > rounded_size) {
33
+ float betta1_minus1 = 1 - _betta1;
34
+ float betta2_minus1 = 1 - _betta2;
35
+
36
+ float step_size = -1 * _alpha / _bias_correction1;
37
+ float w_decay = -1 * _alpha * _weight_decay;
38
+
39
+ for (size_t t = rounded_size; t < _param_size; t += TILE) {
40
+ size_t copy_size = TILE;
41
+ if ((t + TILE) > _param_size) copy_size = _param_size - t;
42
+ size_t offset = copy_size + t;
43
+ #pragma omp parallel for
44
+ for (size_t k = t; k < offset; k++) {
45
+ float grad = (float)grads[k];
46
+ float param = (float)_params[k];
47
+ float momentum = _exp_avg[k];
48
+ float variance = _exp_avg_sq[k];
49
+ if (_weight_decay > 0 && !_adamw_mode) { grad = param * _weight_decay + grad; }
50
+ momentum = momentum * _betta1;
51
+ momentum = grad * betta1_minus1 + momentum;
52
+
53
+ variance = variance * _betta2;
54
+ grad = grad * grad;
55
+ variance = grad * betta2_minus1 + variance;
56
+
57
+ grad = sqrt(variance);
58
+ grad = grad * _bias_correction2 + _eps;
59
+ grad = momentum / grad;
60
+ if (_weight_decay > 0 && _adamw_mode) { param += w_decay * param; }
61
+ param = grad * step_size + param;
62
+ _params[k] = param;
63
+ _exp_avg[k] = momentum;
64
+ _exp_avg_sq[k] = variance;
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
71
+ void Adam_Optimizer::Step_4(ds_params_precision_t* _params,
72
+ ds_params_precision_t* grads,
73
+ ds_state_precision_t* _exp_avg,
74
+ ds_state_precision_t* _exp_avg_sq,
75
+ size_t _param_size)
76
+ {
77
+ size_t rounded_size = 0;
78
+ #if defined(__AVX512__) or defined(__AVX256__)
79
+ Step_AVX<4>(&rounded_size, _params, grads, _exp_avg, _exp_avg_sq, _param_size);
80
+ #endif
81
+ if (_param_size > rounded_size)
82
+ Step_1((_params + rounded_size),
83
+ (grads + rounded_size),
84
+ (_exp_avg + rounded_size),
85
+ (_exp_avg_sq + rounded_size),
86
+ (_param_size - rounded_size));
87
+ }
88
+
89
+ int create_adam_optimizer(int optimizer_id,
90
+ float alpha,
91
+ float betta1,
92
+ float betta2,
93
+ float eps,
94
+ float weight_decay,
95
+ bool adamw_mode,
96
+ bool should_log)
97
+ {
98
+ auto opt =
99
+ std::make_shared<Adam_Optimizer>(alpha, betta1, betta2, eps, weight_decay, adamw_mode);
100
+
101
+ s_optimizers[optimizer_id] = opt;
102
+
103
+ if (should_log) {
104
+ std::string avx_type = "";
105
+ #if defined(__AVX512__)
106
+ avx_type = "AVX512";
107
+ #else
108
+ #if defined(__AVX256__)
109
+ avx_type = "AVX2";
110
+ #else
111
+ avx_type = "scalar";
112
+ #endif
113
+ #endif
114
+
115
+ printf("Adam Optimizer #%d is created with %s arithmetic capability.\n",
116
+ optimizer_id,
117
+ avx_type.c_str());
118
+ printf("Config: alpha=%f, betas=(%f, %f), weight_decay=%f, adam_w=%d\n",
119
+ alpha,
120
+ betta1,
121
+ betta2,
122
+ weight_decay,
123
+ (int)adamw_mode);
124
+ }
125
+
126
+ return 0;
127
+ }
128
+
129
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
130
+ void Adam_Optimizer::Step_8(ds_params_precision_t* _params,
131
+ ds_params_precision_t* grads,
132
+ ds_state_precision_t* _exp_avg,
133
+ ds_state_precision_t* _exp_avg_sq,
134
+ size_t _param_size)
135
+ {
136
+ size_t rounded_size = 0;
137
+ #if defined(__AVX512__) or defined(__AVX256__)
138
+ Step_AVX<8>(&rounded_size, _params, grads, _exp_avg, _exp_avg_sq, _param_size);
139
+ #endif
140
+ if (_param_size > rounded_size)
141
+ Step_4((_params + rounded_size),
142
+ (grads + rounded_size),
143
+ (_exp_avg + rounded_size),
144
+ (_exp_avg_sq + rounded_size),
145
+ (_param_size - rounded_size));
146
+ }
147
+
148
+ template <typename ds_params_precision_t, typename ds_state_precision_t>
149
+ void step_invoker(std::shared_ptr<Adam_Optimizer> opt,
150
+ void* _params,
151
+ void* grads,
152
+ void* _exp_avg,
153
+ void* _exp_avg_sq,
154
+ size_t _param_size)
155
+ {
156
+ opt->Step_8((ds_params_precision_t*)(_params),
157
+ (ds_params_precision_t*)(grads),
158
+ (ds_state_precision_t*)(_exp_avg),
159
+ (ds_state_precision_t*)(_exp_avg_sq),
160
+ _param_size);
161
+ }
162
+
163
+ std::map<std::tuple<c10::ScalarType, c10::ScalarType>,
164
+ std::function<void(std::shared_ptr<Adam_Optimizer>, void*, void*, void*, void*, size_t)>>
165
+ invokers;
166
+
167
+ // Fill map with template functions for each type
168
+ template <class ds_params_precision_t, class ds_state_precision_t>
169
+ void create_invoker()
170
+ {
171
+ invokers[std::tuple(c10::CppTypeToScalarType<ds_params_precision_t>(),
172
+ c10::CppTypeToScalarType<ds_state_precision_t>())] =
173
+ step_invoker<ds_params_precision_t, ds_state_precision_t>;
174
+ }
175
+ struct InvokerInitializer {
176
+ InvokerInitializer()
177
+ {
178
+ create_invoker<c10::Half, float>();
179
+ create_invoker<c10::Half, c10::Half>();
180
+ create_invoker<c10::BFloat16, float>();
181
+ create_invoker<c10::BFloat16, c10::BFloat16>();
182
+ create_invoker<float, float>();
183
+ }
184
+ } _invoker_initializer;
185
+
186
+ void invoke(std::shared_ptr<Adam_Optimizer> opt,
187
+ torch::Tensor& params,
188
+ torch::Tensor& grads,
189
+ torch::Tensor& exp_avg,
190
+ torch::Tensor& exp_avg_sq,
191
+ size_t param_size)
192
+ {
193
+ c10::ScalarType params_type = at::typeMetaToScalarType(params.options().dtype());
194
+ c10::ScalarType state_type = at::typeMetaToScalarType(exp_avg.options().dtype());
195
+
196
+ auto it = invokers.find(std::tuple(params_type, state_type));
197
+ if (it == invokers.end()) {
198
+ throw std::runtime_error("Adam optimizer with param type "s + c10::toString(params_type) +
199
+ " and state type "s + c10::toString(state_type) +
200
+ " is not supported on current hardware"s);
201
+ }
202
+
203
+ it->second(opt,
204
+ params.data_ptr(),
205
+ grads.data_ptr(),
206
+ exp_avg.data_ptr(),
207
+ exp_avg_sq.data_ptr(),
208
+ param_size);
209
+ }
210
+
211
+ int ds_adam_step(int optimizer_id,
212
+ size_t step,
213
+ float lr,
214
+ float beta1,
215
+ float beta2,
216
+ float epsilon,
217
+ float weight_decay,
218
+ bool bias_correction,
219
+ torch::Tensor& params,
220
+ torch::Tensor& grads,
221
+ torch::Tensor& exp_avg,
222
+ torch::Tensor& exp_avg_sq)
223
+ {
224
+ auto params_c = params.contiguous();
225
+ auto grads_c = grads.contiguous();
226
+ auto exp_avg_c = exp_avg.contiguous();
227
+ auto exp_avg_sq_c = exp_avg_sq.contiguous();
228
+
229
+ std::shared_ptr<Adam_Optimizer> opt =
230
+ std::static_pointer_cast<Adam_Optimizer>(s_optimizers[optimizer_id]);
231
+ opt->IncrementStep(step, beta1, beta2);
232
+ opt->update_state(lr, epsilon, weight_decay, bias_correction);
233
+
234
+ invoke(opt, params_c, grads_c, exp_avg_c, exp_avg_sq_c, params_c.numel());
235
+
236
+ return 0;
237
+ }
238
+
239
+ int destroy_adam_optimizer(int optimizer_id)
240
+ {
241
+ s_optimizers.erase(optimizer_id);
242
+
243
+ return 0;
244
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/adam/fused_adam_frontend.cpp ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include <torch/extension.h>
7
+
8
+ void multi_tensor_adam_cuda(int chunk_size,
9
+ at::Tensor noop_flag,
10
+ std::vector<std::vector<at::Tensor>> tensor_lists,
11
+ const float lr,
12
+ const float beta1,
13
+ const float beta2,
14
+ const float epsilon,
15
+ const int step,
16
+ const int mode,
17
+ const int bias_correction,
18
+ const float weight_decay);
19
+
20
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
21
+ {
22
+ m.def("multi_tensor_adam",
23
+ &multi_tensor_adam_cuda,
24
+ "Compute and apply gradient update to parameters for Adam optimizer");
25
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_adam.cu ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Copyright NVIDIA/apex
8
+ This file is adapted from fused adam in NVIDIA/apex, commit a109f85
9
+ */
10
+
11
+ #include <ATen/ATen.h>
12
+ #include <ATen/AccumulateType.h>
13
+ #include <ATen/cuda/CUDAContext.h>
14
+ #include <ATen/cuda/Exceptions.h>
15
+ // Another possibility:
16
+ // #include <torch/all.h>
17
+
18
+ #include <assert.h>
19
+
20
+ #include "multi_tensor_apply.cuh"
21
+ #include "type_shim.h"
22
+
23
+ #define BLOCK_SIZE 512
24
+ #define ILP 4
25
+
26
+ typedef enum : int {
27
+ ADAM_MODE_0 = 0, // L2 regularization mode
28
+ ADAM_MODE_1 = 1 // Decoupled weight decay mode(AdamW)
29
+ } adamMode_t;
30
+
31
+ using MATH_T = float;
32
+
33
+ template <typename T, typename index_t>
34
+ struct AdamFunctor {
35
+ __device__ __forceinline__ void operator()(int chunk_size,
36
+ volatile int* noop_gmem,
37
+ TensorListMetadata<4>& tl,
38
+ const float beta1,
39
+ const float beta2,
40
+ const float beta1_correction,
41
+ const float beta2_correction,
42
+ const float epsilon,
43
+ const float lr,
44
+ adamMode_t mode,
45
+ const float decay)
46
+ {
47
+ // I'd like this kernel to propagate infs/nans.
48
+ // if(*noop_gmem == 1)
49
+ // return;
50
+
51
+ index_t tensor_loc = tl.block_to_tensor[blockIdx.x];
52
+
53
+ // potentially use to pass in list of scalar
54
+ // int tensor_num = tl.start_tensor_this_launch + tensor_loc;
55
+
56
+ index_t chunk_idx = tl.block_to_chunk[blockIdx.x];
57
+ index_t n = tl.sizes[tensor_loc];
58
+
59
+ T* g = (T*)tl.addresses[0][tensor_loc];
60
+ g += chunk_idx * chunk_size;
61
+
62
+ T* p = (T*)tl.addresses[1][tensor_loc];
63
+ p += chunk_idx * chunk_size;
64
+
65
+ T* m = (T*)tl.addresses[2][tensor_loc];
66
+ m += chunk_idx * chunk_size;
67
+
68
+ T* v = (T*)tl.addresses[3][tensor_loc];
69
+ v += chunk_idx * chunk_size;
70
+
71
+ n -= chunk_idx * chunk_size;
72
+
73
+ // see note in multi_tensor_scale_kernel.cu
74
+ for (index_t i_start = 0; i_start < n && i_start < chunk_size;
75
+ i_start += blockDim.x * ILP) {
76
+ MATH_T r_g[ILP];
77
+ MATH_T r_p[ILP];
78
+ MATH_T r_m[ILP];
79
+ MATH_T r_v[ILP];
80
+ #pragma unroll
81
+ for (int ii = 0; ii < ILP; ii++) {
82
+ int i = i_start + threadIdx.x + ii * blockDim.x;
83
+ if (i < n && i < chunk_size) {
84
+ r_g[ii] = g[i];
85
+ r_p[ii] = p[i];
86
+ r_m[ii] = m[i];
87
+ r_v[ii] = v[i];
88
+ } else {
89
+ r_g[ii] = MATH_T(0);
90
+ r_p[ii] = MATH_T(0);
91
+ r_m[ii] = MATH_T(0);
92
+ r_v[ii] = MATH_T(0);
93
+ }
94
+ }
95
+ #pragma unroll
96
+ for (int ii = 0; ii < ILP; ii++) {
97
+ if (mode == ADAM_MODE_0) { // L2
98
+ r_g[ii] = r_g[ii] + (decay * r_p[ii]);
99
+ r_m[ii] = beta1 * r_m[ii] + (1 - beta1) * r_g[ii];
100
+ r_v[ii] = beta2 * r_v[ii] + (1 - beta2) * r_g[ii] * r_g[ii];
101
+ MATH_T next_m_unbiased = r_m[ii] / beta1_correction;
102
+ MATH_T next_v_unbiased = r_v[ii] / beta2_correction;
103
+ MATH_T denom = sqrtf(next_v_unbiased) + epsilon;
104
+ MATH_T update = next_m_unbiased / denom;
105
+ r_p[ii] = r_p[ii] - (lr * update);
106
+ } else { // weight decay
107
+ r_m[ii] = beta1 * r_m[ii] + (1 - beta1) * r_g[ii];
108
+ r_v[ii] = beta2 * r_v[ii] + (1 - beta2) * r_g[ii] * r_g[ii];
109
+ MATH_T next_m_unbiased = r_m[ii] / beta1_correction;
110
+ MATH_T next_v_unbiased = r_v[ii] / beta2_correction;
111
+ MATH_T denom = sqrtf(next_v_unbiased) + epsilon;
112
+ MATH_T update = (next_m_unbiased / denom) + (decay * r_p[ii]);
113
+ r_p[ii] = r_p[ii] - (lr * update);
114
+ }
115
+ }
116
+ #pragma unroll
117
+ for (int ii = 0; ii < ILP; ii++) {
118
+ int i = i_start + threadIdx.x + ii * blockDim.x;
119
+ if (i < n && i < chunk_size) {
120
+ p[i] = r_p[ii];
121
+ m[i] = r_m[ii];
122
+ v[i] = r_v[ii];
123
+ }
124
+ }
125
+ }
126
+ }
127
+ };
128
+
129
+ void multi_tensor_adam_cuda(int chunk_size,
130
+ at::Tensor noop_flag,
131
+ std::vector<std::vector<at::Tensor>> tensor_lists,
132
+ const float lr,
133
+ const float beta1,
134
+ const float beta2,
135
+ const float epsilon,
136
+ const int step,
137
+ const int mode,
138
+ const int bias_correction,
139
+ const float weight_decay)
140
+ {
141
+ using namespace at;
142
+
143
+ // Handle bias correction mode
144
+ float bias_correction1 = 1.0f, bias_correction2 = 1.0f;
145
+ if (bias_correction == 1) {
146
+ bias_correction1 = 1 - std::pow(beta1, step);
147
+ bias_correction2 = 1 - std::pow(beta2, step);
148
+ }
149
+
150
+ size_t max_size = 0;
151
+ bool requires_64bit_indexing = false;
152
+ for (auto it = tensor_lists.begin(); it != tensor_lists.end(); it++) {
153
+ for (auto it2 = it->begin(); it2 != it->end(); it2++) {
154
+ if (it2->numel() > max_size) {
155
+ max_size = it2->numel();
156
+ if (max_size >= INT_MAX) {
157
+ requires_64bit_indexing = true;
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ if (requires_64bit_indexing) { break; }
163
+ }
164
+
165
+ // Assume single type across p,g,m1,m2 now
166
+ if (requires_64bit_indexing) {
167
+ DISPATCH_DOUBLE_FLOAT_AND_HALF(tensor_lists[0][0].scalar_type(),
168
+ 0,
169
+ "adam",
170
+ multi_tensor_apply<4>((int64_t)BLOCK_SIZE,
171
+ (int64_t)chunk_size,
172
+ noop_flag,
173
+ tensor_lists,
174
+ AdamFunctor<scalar_t_0, int64_t>(),
175
+ beta1,
176
+ beta2,
177
+ bias_correction1,
178
+ bias_correction2,
179
+ epsilon,
180
+ lr,
181
+ (adamMode_t)mode,
182
+ weight_decay);)
183
+ } else {
184
+ DISPATCH_DOUBLE_FLOAT_AND_HALF(tensor_lists[0][0].scalar_type(),
185
+ 0,
186
+ "adam",
187
+ multi_tensor_apply<4>(BLOCK_SIZE,
188
+ chunk_size,
189
+ noop_flag,
190
+ tensor_lists,
191
+ AdamFunctor<scalar_t_0, int32_t>(),
192
+ beta1,
193
+ beta2,
194
+ bias_correction1,
195
+ bias_correction2,
196
+ epsilon,
197
+ lr,
198
+ (adamMode_t)mode,
199
+ weight_decay);)
200
+ }
201
+
202
+ AT_CUDA_CHECK(cudaGetLastError());
203
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_apply.cuh ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Copyright NVIDIA/apex
8
+ This file is adapted from fused adam in NVIDIA/apex, commit a109f85
9
+ */
10
+
11
+ #include <ATen/ATen.h>
12
+ #include <ATen/AccumulateType.h>
13
+ #include <ATen/cuda/CUDAContext.h>
14
+ #include <ATen/cuda/Exceptions.h>
15
+ #include <c10/cuda/CUDAGuard.h>
16
+ #include "compat.h"
17
+
18
+ #include <assert.h>
19
+
20
+ // #include <iostream>
21
+
22
+ // This header is the one-stop shop for all your multi-tensor apply needs.
23
+
24
+ // TODO: Kernel arg size limit may be <4KB for some other cards (ie Jetson)
25
+ constexpr int depth_to_max_tensors[5] = {110, 64, 48, 36, 30};
26
+ constexpr int depth_to_max_blocks[5] = {320, 320, 320, 320, 320};
27
+
28
+ template <int n>
29
+ struct TensorListMetadata {
30
+ void* addresses[n][depth_to_max_tensors[n - 1]];
31
+ int sizes[depth_to_max_tensors[n - 1]];
32
+ unsigned char block_to_tensor[depth_to_max_blocks[n - 1]];
33
+ int block_to_chunk[depth_to_max_blocks[n - 1]]; // I fear this needs to be a full int.
34
+ int start_tensor_this_launch;
35
+ };
36
+
37
+ template <typename T, typename U, typename... ArgTypes>
38
+ __global__ void multi_tensor_apply_kernel(int64_t chunk_size,
39
+ volatile int* noop_flag,
40
+ T tl,
41
+ U callable,
42
+ ArgTypes... args)
43
+ {
44
+ // Hand the chunk information to the user-supplied functor to process however it likes.
45
+ callable(chunk_size, noop_flag, tl, args...);
46
+ }
47
+
48
+ template <int depth, typename T, typename... ArgTypes>
49
+ void multi_tensor_apply(int64_t block_size,
50
+ int64_t chunk_size,
51
+ const at::Tensor& noop_flag,
52
+ const std::vector<std::vector<at::Tensor>>& tensor_lists,
53
+ T callable,
54
+ ArgTypes... args)
55
+ {
56
+ TORCH_CHECK(tensor_lists.size() == depth, "tensor_lists.size() != depth");
57
+ int len0 = tensor_lists[0].size();
58
+ TORCH_CHECK(len0 > 0, "tensor_lists[0].size() is not > 0");
59
+ auto ref_device = tensor_lists[0][0].device();
60
+ TORCH_CHECK(ref_device.type() == at::kCUDA, "expected input to be on cuda");
61
+ for (int l = 0; l < tensor_lists.size(); l++) // No range-based for because I need indices
62
+ {
63
+ TORCH_CHECK(tensor_lists[l].size() == len0, "Size mismatch among tensor lists");
64
+ for (int t = 0; t < tensor_lists[l].size(); t++) {
65
+ // TODO: Print which tensor fails.
66
+ bool contiguous_memory = tensor_lists[l][t].is_contiguous();
67
+ #ifdef VERSION_GE_1_5
68
+ contiguous_memory = (contiguous_memory ||
69
+ tensor_lists[l][t].is_contiguous(at::MemoryFormat::ChannelsLast));
70
+ #endif
71
+ TORCH_CHECK(contiguous_memory, "A tensor was not contiguous.");
72
+ TORCH_CHECK(tensor_lists[l][t].device() == ref_device,
73
+ "A tensor was not on the same device as the first tensor");
74
+ TORCH_CHECK(tensor_lists[l][t].numel() == tensor_lists[0][t].numel(), "Size mismatch");
75
+ }
76
+ }
77
+
78
+ int ntensors = tensor_lists[0].size();
79
+
80
+ TensorListMetadata<depth> tl;
81
+
82
+ const at::cuda::OptionalCUDAGuard device_guard(device_of(tensor_lists[0][0]));
83
+ auto stream = at::cuda::getCurrentCUDAStream();
84
+
85
+ tl.start_tensor_this_launch = 0;
86
+ int loc_block_info = 0;
87
+ int loc_tensor_info = 0;
88
+ for (int t = 0; t < ntensors; t++) {
89
+ tl.sizes[loc_tensor_info] = tensor_lists[0][t].numel();
90
+ for (int d = 0; d < depth; d++)
91
+ tl.addresses[d][loc_tensor_info] = tensor_lists[d][t].data_ptr();
92
+ loc_tensor_info++;
93
+
94
+ auto chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;
95
+
96
+ for (auto chunk = 0; chunk < chunks_this_tensor; chunk++) {
97
+ // std::cout << chunks_this_tensor << std::endl;
98
+ tl.block_to_tensor[loc_block_info] = loc_tensor_info - 1;
99
+ tl.block_to_chunk[loc_block_info] = chunk;
100
+ loc_block_info++;
101
+
102
+ bool tensors_full = (loc_tensor_info == depth_to_max_tensors[depth - 1] &&
103
+ chunk == chunks_this_tensor - 1);
104
+ bool blocks_full = (loc_block_info == depth_to_max_blocks[depth - 1]);
105
+ bool last_chunk = (t == ntensors - 1 && chunk == chunks_this_tensor - 1);
106
+ if (tensors_full || blocks_full || last_chunk) {
107
+ // using accscalar_t = acc_type<scalar_t, true>;
108
+ multi_tensor_apply_kernel<<<loc_block_info, block_size, 0, stream>>>(
109
+ chunk_size, noop_flag.DATA_PTR<int>(), tl, callable, args...);
110
+
111
+ AT_CUDA_CHECK(cudaGetLastError());
112
+
113
+ // Reset. The control flow possibilities here make my brain hurt.
114
+ loc_block_info = 0;
115
+ if (chunk == chunks_this_tensor - 1) {
116
+ // std::cout << "Hit case 1 " << cond1 << " " << cond2 << " " << cond3 <<
117
+ // std::endl;
118
+ loc_tensor_info = 0;
119
+ tl.start_tensor_this_launch = t + 1;
120
+ } else {
121
+ // std::cout << "Hit case 2 " << cond1 << " " << cond2 << " " << cond3 <<
122
+ // std::endl;
123
+ tl.sizes[0] = tl.sizes[loc_tensor_info - 1];
124
+ for (int d = 0; d < depth; d++)
125
+ tl.addresses[d][0] = tl.addresses[d][loc_tensor_info - 1];
126
+ loc_tensor_info = 1;
127
+ tl.start_tensor_this_launch = t;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.cpp ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <assert.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+
14
+ #include <fcntl.h>
15
+ #include <libaio.h>
16
+ #include <sys/mman.h>
17
+ #include <sys/stat.h>
18
+ #include <sys/types.h>
19
+ #include <time.h>
20
+ #include <unistd.h>
21
+
22
+ #include <algorithm>
23
+ #include <chrono>
24
+ #include <cmath>
25
+ #include <cstring>
26
+ #include <fstream>
27
+ #include <iostream>
28
+ #include <limits>
29
+ #include <numeric>
30
+ #include <string>
31
+ #include <vector>
32
+
33
+ #include "deepspeed_aio_common.h"
34
+
35
+ using namespace std;
36
+ using namespace std::chrono;
37
+
38
+ #define DEBUG_DS_AIO_PERF 0
39
+ #define DEBUG_DS_AIO_SUBMIT_PERF 0
40
+
41
+ static const std::string c_library_name = "deepspeed_aio";
42
+
43
+ static void _report_aio_statistics(const char* tag,
44
+ const std::vector<std::chrono::duration<double>>& latencies)
45
+ __attribute__((unused));
46
+
47
+ static void _report_aio_statistics(const char* tag,
48
+ const std::vector<std::chrono::duration<double>>& latencies)
49
+ {
50
+ std::vector<double> lat_usec;
51
+ for (auto& lat : latencies) { lat_usec.push_back(lat.count() * 1e6); }
52
+ const auto min_lat = *(std::min_element(lat_usec.begin(), lat_usec.end()));
53
+ const auto max_lat = *(std::max_element(lat_usec.begin(), lat_usec.end()));
54
+ const auto avg_lat = std::accumulate(lat_usec.begin(), lat_usec.end(), 0) / lat_usec.size();
55
+
56
+ std::cout << c_library_name << ": latency statistics(usec) " << tag
57
+ << " min/max/avg = " << min_lat << " " << max_lat << " " << avg_lat << std::endl;
58
+ }
59
+
60
+ static void _get_aio_latencies(std::vector<std::chrono::duration<double>>& raw_latencies,
61
+ struct deepspeed_aio_latency_t& summary_latencies)
62
+ {
63
+ std::vector<double> lat_usec;
64
+ for (auto& lat : raw_latencies) { lat_usec.push_back(lat.count() * 1e6); }
65
+ summary_latencies._min_usec = *(std::min_element(lat_usec.begin(), lat_usec.end()));
66
+ summary_latencies._max_usec = *(std::max_element(lat_usec.begin(), lat_usec.end()));
67
+ summary_latencies._avg_usec =
68
+ std::accumulate(lat_usec.begin(), lat_usec.end(), 0) / lat_usec.size();
69
+ }
70
+
71
+ static void _do_io_submit_singles(const int64_t n_iocbs,
72
+ const int64_t iocb_index,
73
+ std::unique_ptr<aio_context>& aio_ctxt,
74
+ std::vector<std::chrono::duration<double>>& submit_times)
75
+ {
76
+ for (auto i = 0; i < n_iocbs; ++i) {
77
+ const auto st = std::chrono::high_resolution_clock::now();
78
+ const auto submit_ret = io_submit(aio_ctxt->_io_ctxt, 1, aio_ctxt->_iocbs.data() + i);
79
+ submit_times.push_back(std::chrono::high_resolution_clock::now() - st);
80
+ #if DEBUG_DS_AIO_SUBMIT_PERF
81
+ printf("submit(usec) %f io_index=%lld buf=%p len=%lu off=%llu \n",
82
+ submit_times.back().count() * 1e6,
83
+ iocb_index,
84
+ aio_ctxt->_iocbs[i]->u.c.buf,
85
+ aio_ctxt->_iocbs[i]->u.c.nbytes,
86
+ aio_ctxt->_iocbs[i]->u.c.offset);
87
+ #endif
88
+ assert(submit_ret > 0);
89
+ }
90
+ }
91
+
92
+ static void _do_io_submit_block(const int64_t n_iocbs,
93
+ const int64_t iocb_index,
94
+ std::unique_ptr<aio_context>& aio_ctxt,
95
+ std::vector<std::chrono::duration<double>>& submit_times)
96
+ {
97
+ const auto st = std::chrono::high_resolution_clock::now();
98
+ const auto submit_ret = io_submit(aio_ctxt->_io_ctxt, n_iocbs, aio_ctxt->_iocbs.data());
99
+ submit_times.push_back(std::chrono::high_resolution_clock::now() - st);
100
+ #if DEBUG_DS_AIO_SUBMIT_PERF
101
+ printf("submit(usec) %f io_index=%lld nr=%lld buf=%p len=%lu off=%llu \n",
102
+ submit_times.back().count() * 1e6,
103
+ iocb_index,
104
+ n_iocbs,
105
+ aio_ctxt->_iocbs[0]->u.c.buf,
106
+ aio_ctxt->_iocbs[0]->u.c.nbytes,
107
+ aio_ctxt->_iocbs[0]->u.c.offset);
108
+ #endif
109
+ assert(submit_ret > 0);
110
+ }
111
+
112
+ static int _do_io_complete(const int64_t min_completes,
113
+ const int64_t max_completes,
114
+ std::unique_ptr<aio_context>& aio_ctxt,
115
+ std::vector<std::chrono::duration<double>>& reap_times)
116
+ {
117
+ const auto start_time = std::chrono::high_resolution_clock::now();
118
+ int64_t n_completes = io_pgetevents(aio_ctxt->_io_ctxt,
119
+ min_completes,
120
+ max_completes,
121
+ aio_ctxt->_io_events.data(),
122
+ nullptr,
123
+ nullptr);
124
+ reap_times.push_back(std::chrono::high_resolution_clock::now() - start_time);
125
+ assert(n_completes >= min_completes);
126
+ return n_completes;
127
+ }
128
+
129
+ void do_aio_operation_sequential(const bool read_op,
130
+ std::unique_ptr<aio_context>& aio_ctxt,
131
+ std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
132
+ deepspeed_aio_config_t* config,
133
+ deepspeed_aio_perf_t* perf)
134
+ {
135
+ struct io_prep_context prep_ctxt(read_op, xfer_ctxt, aio_ctxt->_block_size, &aio_ctxt->_iocbs);
136
+
137
+ const auto num_io_blocks = static_cast<int64_t>(
138
+ ceil(static_cast<double>(xfer_ctxt->_num_bytes) / aio_ctxt->_block_size));
139
+ #if DEBUG_DS_AIO_PERF
140
+ const auto io_op_name = std::string(read_op ? "read" : "write");
141
+ std::cout << c_library_name << ": start " << io_op_name << " " << xfer_ctxt->_num_bytes
142
+ << " bytes with " << num_io_blocks << " io blocks" << std::endl;
143
+ #endif
144
+
145
+ std::vector<std::chrono::duration<double>> submit_times;
146
+ std::vector<std::chrono::duration<double>> reap_times;
147
+ const auto max_queue_bytes =
148
+ static_cast<int64_t>(aio_ctxt->_queue_depth * aio_ctxt->_block_size);
149
+
150
+ auto start = std::chrono::high_resolution_clock::now();
151
+ for (int64_t iocb_index = 0; iocb_index < num_io_blocks; iocb_index += aio_ctxt->_queue_depth) {
152
+ const auto start_offset = iocb_index * aio_ctxt->_block_size;
153
+ const auto start_buffer = (char*)xfer_ctxt->_mem_buffer + start_offset;
154
+ const auto n_iocbs =
155
+ min(static_cast<int64_t>(aio_ctxt->_queue_depth), (num_io_blocks - iocb_index));
156
+ const auto num_bytes = min(max_queue_bytes, (xfer_ctxt->_num_bytes - start_offset));
157
+ prep_ctxt.prep_iocbs(n_iocbs, num_bytes, start_buffer, start_offset);
158
+
159
+ if (config->_single_submit) {
160
+ _do_io_submit_singles(n_iocbs, iocb_index, aio_ctxt, submit_times);
161
+ } else {
162
+ _do_io_submit_block(n_iocbs, iocb_index, aio_ctxt, submit_times);
163
+ }
164
+
165
+ _do_io_complete(n_iocbs, n_iocbs, aio_ctxt, reap_times);
166
+ }
167
+ const std::chrono::duration<double> elapsed = std::chrono::high_resolution_clock::now() - start;
168
+
169
+ if (perf) {
170
+ _get_aio_latencies(submit_times, perf->_submit);
171
+ _get_aio_latencies(reap_times, perf->_complete);
172
+ perf->_e2e_usec = elapsed.count() * 1e6;
173
+ perf->_e2e_rate_GB = (xfer_ctxt->_num_bytes / elapsed.count() / 1e9);
174
+ }
175
+
176
+ #if DEBUG_DS_AIO_PERF
177
+ _report_aio_statistics("submit", submit_times);
178
+ _report_aio_statistics("complete", reap_times);
179
+ #endif
180
+
181
+ #if DEBUG_DS_AIO_PERF
182
+ std::cout << c_library_name << ": runtime(usec) " << elapsed.count() * 1e6
183
+ << " rate(GB/sec) = " << (xfer_ctxt->_num_bytes / elapsed.count() / 1e9) << std::endl;
184
+ #endif
185
+
186
+ #if DEBUG_DS_AIO_PERF
187
+ std::cout << c_library_name << ": finish " << io_op_name << " " << xfer_ctxt->_num_bytes
188
+ << " bytes " << std::endl;
189
+ #endif
190
+ }
191
+
192
+ void do_aio_operation_overlap(const bool read_op,
193
+ std::unique_ptr<aio_context>& aio_ctxt,
194
+ std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
195
+ deepspeed_aio_config_t* config,
196
+ deepspeed_aio_perf_t* perf)
197
+ {
198
+ struct io_prep_generator io_gen(read_op, xfer_ctxt, aio_ctxt->_block_size);
199
+
200
+ #if DEBUG_DS_AIO_PERF
201
+ const auto io_op_name = std::string(read_op ? "read" : "write");
202
+ std::cout << c_library_name << ": start " << io_op_name << " " << xfer_ctxt->_num_bytes
203
+ << " bytes with " << io_gen._num_io_blocks << " io blocks" << std::endl;
204
+ #endif
205
+
206
+ std::vector<std::chrono::duration<double>> submit_times;
207
+ std::vector<std::chrono::duration<double>> reap_times;
208
+
209
+ auto request_iocbs = aio_ctxt->_queue_depth;
210
+ auto n_pending_iocbs = 0;
211
+ const auto min_completes = 1;
212
+ auto start = std::chrono::high_resolution_clock::now();
213
+ while (true) {
214
+ const auto n_iocbs = io_gen.prep_iocbs(request_iocbs - n_pending_iocbs, &aio_ctxt->_iocbs);
215
+ if (n_iocbs > 0) {
216
+ if (config->_single_submit) {
217
+ _do_io_submit_singles(
218
+ n_iocbs, (io_gen._next_iocb_index - n_iocbs), aio_ctxt, submit_times);
219
+ } else {
220
+ _do_io_submit_block(
221
+ n_iocbs, (io_gen._next_iocb_index - n_iocbs), aio_ctxt, submit_times);
222
+ }
223
+ }
224
+
225
+ n_pending_iocbs += n_iocbs;
226
+ assert(n_pending_iocbs <= aio_ctxt->_queue_depth);
227
+
228
+ if (n_pending_iocbs == 0) { break; }
229
+
230
+ const auto n_complete =
231
+ _do_io_complete(min_completes, n_pending_iocbs, aio_ctxt, reap_times);
232
+ n_pending_iocbs -= n_complete;
233
+ }
234
+
235
+ const std::chrono::duration<double> elapsed = std::chrono::high_resolution_clock::now() - start;
236
+
237
+ if (perf) {
238
+ _get_aio_latencies(submit_times, perf->_submit);
239
+ _get_aio_latencies(reap_times, perf->_complete);
240
+ perf->_e2e_usec = elapsed.count() * 1e6;
241
+ perf->_e2e_rate_GB = (xfer_ctxt->_num_bytes / elapsed.count() / 1e9);
242
+ }
243
+
244
+ #if DEBUG_DS_AIO_PERF
245
+ _report_aio_statistics("submit", submit_times);
246
+ _report_aio_statistics("complete", reap_times);
247
+ #endif
248
+
249
+ #if DEBUG_DS_AIO_PERF
250
+ std::cout << c_library_name << ": runtime(usec) " << elapsed.count() * 1e6
251
+ << " rate(GB/sec) = " << (xfer_ctxt->_num_bytes / elapsed.count() / 1e9) << std::endl;
252
+ #endif
253
+
254
+ #if DEBUG_DS_AIO_PERF
255
+ std::cout << c_library_name << ": finish " << io_op_name << " " << xfer_ctxt->_num_bytes
256
+ << " bytes " << std::endl;
257
+ #endif
258
+ }
259
+
260
+ void report_file_error(const char* filename, const std::string file_op, const int error_code)
261
+ {
262
+ std::string err_msg = file_op + std::string(" failed on ") + std::string(filename) +
263
+ " error = " + std::to_string(error_code);
264
+ std::cerr << c_library_name << ": " << err_msg << std::endl;
265
+ }
266
+
267
+ int open_file(const char* filename, const bool read_op)
268
+ {
269
+ const int flags = read_op ? (O_RDONLY | O_DIRECT) : (O_WRONLY | O_CREAT | O_DIRECT);
270
+ #if defined(__ENABLE_CANN__)
271
+ int* flags_ptr = (int*)&flags;
272
+ *flags_ptr = read_op ? (O_RDONLY) : (O_WRONLY | O_CREAT);
273
+ #endif
274
+ const int mode = 0600;
275
+ const auto fd = open(filename, flags, mode);
276
+ if (fd == -1) {
277
+ const auto error_code = errno;
278
+ const auto error_msg = read_op ? " open for read " : " open for write ";
279
+ report_file_error(filename, error_msg, error_code);
280
+ return -1;
281
+ }
282
+ return fd;
283
+ }
284
+
285
+ int regular_read(const char* filename, std::vector<char>& buffer)
286
+ {
287
+ const auto fd = open(filename, O_RDONLY, 0600);
288
+ assert(fd != -1);
289
+ struct stat fs;
290
+ const auto result = fstat(fd, &fs);
291
+ assert(result != -1);
292
+ int64_t num_bytes = fs.st_size;
293
+ buffer.resize(num_bytes);
294
+ int64_t read_bytes = 0;
295
+ auto r = 0;
296
+ do {
297
+ const auto buffer_ptr = buffer.data() + read_bytes;
298
+ const auto bytes_to_read = num_bytes - read_bytes;
299
+ r = read(fd, buffer_ptr, bytes_to_read);
300
+ read_bytes += r;
301
+ } while (r > 0);
302
+
303
+ if (read_bytes != num_bytes) {
304
+ std::cerr << "read error " << " read_bytes (read) = " << read_bytes
305
+ << " num_bytes (fstat) = " << num_bytes << std::endl;
306
+ }
307
+ assert(read_bytes == num_bytes);
308
+ close(fd);
309
+ return 0;
310
+ }
311
+
312
+ static bool _validate_buffer(const char* filename, void* aio_buffer, const int64_t num_bytes)
313
+ {
314
+ std::vector<char> regular_buffer;
315
+ const auto reg_ret = regular_read(filename, regular_buffer);
316
+ assert(0 == reg_ret);
317
+ std::cout << "regular read of " << filename << " returned " << regular_buffer.size() << " bytes"
318
+ << std::endl;
319
+
320
+ if (static_cast<int64_t>(regular_buffer.size()) != num_bytes) { return false; }
321
+
322
+ return (0 == memcmp(aio_buffer, regular_buffer.data(), regular_buffer.size()));
323
+ }
324
+
325
+ bool validate_aio_operation(const bool read_op,
326
+ const char* filename,
327
+ void* aio_buffer,
328
+ const int64_t num_bytes)
329
+ {
330
+ const auto msg_suffix = std::string("deepspeed_aio_") +
331
+ std::string(read_op ? "read()" : "write()") +
332
+ std::string("using read()");
333
+
334
+ if (false == _validate_buffer(filename, aio_buffer, num_bytes)) {
335
+ std::cout << "Fail: correctness of " << msg_suffix << std::endl;
336
+ return false;
337
+ }
338
+
339
+ std::cout << "Pass: correctness of " << msg_suffix << std::endl;
340
+ return true;
341
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.h ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <deepspeed_aio_utils.h>
11
+ #include <stdlib.h>
12
+ #include <memory>
13
+ #include <string>
14
+
15
+ using namespace std;
16
+
17
+ void do_aio_operation_sequential(const bool read_op,
18
+ std::unique_ptr<aio_context>& aio_ctxt,
19
+ std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
20
+ deepspeed_aio_config_t* config,
21
+ deepspeed_aio_perf_t* perf);
22
+
23
+ void do_aio_operation_overlap(const bool read_op,
24
+ std::unique_ptr<aio_context>& aio_ctxt,
25
+ std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
26
+ deepspeed_aio_config_t* config,
27
+ deepspeed_aio_perf_t* perf);
28
+
29
+ int open_file(const char* filename, const bool read_op);
30
+
31
+ void report_file_error(const char* filename, const std::string file_op, const int error_code);
32
+
33
+ int regular_read(const char* filename, std::vector<char>& buffer);
34
+
35
+ bool validate_aio_operation(const bool read_op,
36
+ const char* filename,
37
+ void* aio_buffer,
38
+ const int64_t num_bytes);
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.cpp ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <cmath>
11
+
12
+ #include "deepspeed_aio_utils.h"
13
+
14
+ using namespace std;
15
+
16
+ const int c_block_size = 128 * 1024;
17
+ const int c_io_queue_depth = 8;
18
+
19
+ deepspeed_aio_config_t::deepspeed_aio_config_t()
20
+ : _block_size(c_block_size),
21
+ _queue_depth(c_io_queue_depth),
22
+ _single_submit(false),
23
+ _overlap_events(false),
24
+ _lock_memory(false)
25
+ {
26
+ }
27
+
28
+ deepspeed_aio_config_t::deepspeed_aio_config_t(const int block_size,
29
+ const int queue_depth,
30
+ const bool single_submit,
31
+ const bool overlap_events,
32
+ const bool lock_memory)
33
+ : _block_size(block_size),
34
+ _queue_depth(queue_depth),
35
+ _single_submit(single_submit),
36
+ _overlap_events(overlap_events),
37
+ _lock_memory(lock_memory)
38
+ {
39
+ }
40
+
41
+ void deepspeed_aio_latency_t::dump(const std::string tag)
42
+ {
43
+ std::cout << tag << _min_usec << " " << _max_usec << " " << _avg_usec << " " << std::endl;
44
+ }
45
+
46
+ void deepspeed_aio_latency_t::accumulate(const struct deepspeed_aio_latency_t& other)
47
+ {
48
+ _min_usec += other._min_usec;
49
+ _max_usec += other._max_usec;
50
+ _avg_usec += other._avg_usec;
51
+ }
52
+
53
+ void deepspeed_aio_latency_t::scale(const float scaler)
54
+ {
55
+ _min_usec *= scaler;
56
+ _max_usec *= scaler;
57
+ _avg_usec *= scaler;
58
+ }
59
+
60
+ aio_context::aio_context(const int block_size, const int queue_depth)
61
+ {
62
+ _block_size = block_size;
63
+ _queue_depth = queue_depth;
64
+ for (auto i = 0; i < queue_depth; ++i) {
65
+ _iocbs.push_back((struct iocb*)calloc(1, sizeof(struct iocb)));
66
+ }
67
+ _io_events.resize(queue_depth);
68
+ io_queue_init(queue_depth, &_io_ctxt);
69
+ }
70
+
71
+ aio_context::~aio_context()
72
+ {
73
+ for (auto& iocb : _iocbs) { free(iocb); }
74
+ _io_events.resize(0);
75
+ io_queue_release(_io_ctxt);
76
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.h ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <libaio.h>
11
+ #include <stdlib.h>
12
+
13
+ #include <string>
14
+ #include <vector>
15
+
16
+ using namespace std;
17
+
18
+ struct deepspeed_aio_latency_t {
19
+ double _min_usec;
20
+ double _max_usec;
21
+ double _avg_usec;
22
+
23
+ void dump(const std::string tag);
24
+ void accumulate(const deepspeed_aio_latency_t&);
25
+ void scale(const float value);
26
+ };
27
+
28
+ struct deepspeed_aio_perf_t {
29
+ deepspeed_aio_latency_t _submit;
30
+ deepspeed_aio_latency_t _complete;
31
+ double _e2e_usec;
32
+ double _e2e_rate_GB;
33
+ };
34
+
35
+ struct deepspeed_aio_config_t {
36
+ const int _block_size;
37
+ const int _queue_depth;
38
+ const bool _single_submit;
39
+ const bool _overlap_events;
40
+ const bool _lock_memory;
41
+
42
+ deepspeed_aio_config_t();
43
+ deepspeed_aio_config_t(const int block_size,
44
+ const int queue_depth,
45
+ const bool single_submit,
46
+ const bool overlap_events,
47
+ const bool lock_memory);
48
+ };
49
+
50
+ struct aio_context {
51
+ io_context_t _io_ctxt;
52
+ std::vector<struct io_event> _io_events;
53
+ std::vector<struct iocb*> _iocbs;
54
+ int _block_size;
55
+ int _queue_depth;
56
+
57
+ aio_context(const int block_size, const int queue_depth);
58
+ ~aio_context();
59
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.cpp ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <cmath>
11
+ #include <iostream>
12
+
13
+ #include "deepspeed_aio_utils.h"
14
+
15
+ using namespace std;
16
+
17
+ const int c_block_size = 128 * 1024;
18
+ const int c_io_queue_depth = 8;
19
+
20
+ io_xfer_ctxt::io_xfer_ctxt(const int fd,
21
+ const int64_t file_offset,
22
+ const int64_t buffer_offset,
23
+ const int64_t num_bytes,
24
+ const void* buffer)
25
+ : _fd(fd),
26
+ _file_base_offset(file_offset),
27
+ _buffer_base_offset(buffer_offset),
28
+ _mem_buffer(buffer),
29
+ _num_bytes(num_bytes)
30
+ {
31
+ }
32
+
33
+ io_prep_context::io_prep_context(const bool read_op,
34
+ const std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
35
+ const size_t block_size,
36
+ const std::vector<struct iocb*>* iocbs)
37
+ : _read_op(read_op), _xfer_ctxt(xfer_ctxt), _block_size(block_size), _iocbs(iocbs)
38
+ {
39
+ }
40
+
41
+ void io_prep_context::prep_iocbs(const int n_iocbs,
42
+ const size_t num_bytes,
43
+ const void* start_buffer,
44
+ const int64_t start_offset)
45
+ {
46
+ assert(static_cast<size_t>(n_iocbs) <= _iocbs->size());
47
+ for (auto i = 0; i < n_iocbs; ++i) {
48
+ const auto shift = i * _block_size;
49
+ const auto xfer_buffer = (char*)start_buffer + _xfer_ctxt->_buffer_base_offset + shift;
50
+ const auto xfer_offset = _xfer_ctxt->_file_base_offset + start_offset + shift;
51
+ auto byte_count = _block_size;
52
+
53
+ if ((shift + _block_size) > num_bytes) { byte_count = num_bytes - shift; }
54
+
55
+ if (_read_op) {
56
+ io_prep_pread(_iocbs->at(i), _xfer_ctxt->_fd, xfer_buffer, byte_count, xfer_offset);
57
+ } else {
58
+ io_prep_pwrite(_iocbs->at(i), _xfer_ctxt->_fd, xfer_buffer, byte_count, xfer_offset);
59
+ }
60
+ }
61
+ }
62
+
63
+ io_prep_generator::io_prep_generator(const bool read_op,
64
+ const std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
65
+ const size_t block_size)
66
+ : _read_op(read_op),
67
+ _xfer_ctxt(xfer_ctxt),
68
+ _block_size(block_size),
69
+ _remaining_bytes(xfer_ctxt->_num_bytes),
70
+ _next_iocb_index(0)
71
+ {
72
+ _num_io_blocks =
73
+ static_cast<int64_t>(ceil(static_cast<double>(xfer_ctxt->_num_bytes) / block_size));
74
+ _remaining_io_blocks = _num_io_blocks;
75
+ }
76
+
77
+ int io_prep_generator::prep_iocbs(const int n_iocbs, std::vector<struct iocb*>* iocbs)
78
+ {
79
+ if ((_remaining_bytes) == 0 || (_remaining_io_blocks == 0)) {
80
+ assert(static_cast<int64_t>(_remaining_bytes) == _remaining_io_blocks);
81
+ return 0;
82
+ }
83
+
84
+ assert(static_cast<size_t>(n_iocbs) <= iocbs->size());
85
+
86
+ auto actual_n_iocbs = min(static_cast<int64_t>(n_iocbs), _remaining_io_blocks);
87
+ for (auto i = 0; i < actual_n_iocbs; ++i, ++_next_iocb_index) {
88
+ const auto xfer_buffer = (char*)_xfer_ctxt->_mem_buffer + _xfer_ctxt->_buffer_base_offset +
89
+ (_next_iocb_index * _block_size);
90
+ const auto xfer_offset = _xfer_ctxt->_file_base_offset + (_next_iocb_index * _block_size);
91
+ const auto num_bytes = min(static_cast<int64_t>(_block_size), _remaining_bytes);
92
+ if (_read_op) {
93
+ io_prep_pread(iocbs->at(i), _xfer_ctxt->_fd, xfer_buffer, num_bytes, xfer_offset);
94
+ } else {
95
+ io_prep_pwrite(iocbs->at(i), _xfer_ctxt->_fd, xfer_buffer, num_bytes, xfer_offset);
96
+ }
97
+ _remaining_bytes -= num_bytes;
98
+ }
99
+ _remaining_io_blocks -= actual_n_iocbs;
100
+
101
+ return actual_n_iocbs;
102
+ }
103
+
104
+ int get_file_size(const char* filename, int64_t& size)
105
+ {
106
+ struct stat st;
107
+ if (stat(filename, &st) == -1) { return -1; }
108
+ size = st.st_size;
109
+ return 0;
110
+ }
111
+
112
+ void* ds_page_aligned_alloc(const int64_t size, const bool lock)
113
+ {
114
+ void* ptr;
115
+ int retval;
116
+
117
+ retval = posix_memalign(&ptr, (size_t)sysconf(_SC_PAGESIZE), size);
118
+ if (retval) { return nullptr; }
119
+
120
+ if (lock == false) { return ptr; }
121
+
122
+ auto mlock_ret = mlock(ptr, size);
123
+ if (mlock_ret != 0) {
124
+ auto mlock_error = errno;
125
+ std::cerr << "mlock failed to allocate " << size << " bytes with error no " << mlock_error
126
+ << " msg " << strerror(mlock_error) << std::endl;
127
+ free(ptr);
128
+ return nullptr;
129
+ }
130
+
131
+ return ptr;
132
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.h ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #pragma once
11
+
12
+ #include <assert.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+
16
+ #include <fcntl.h>
17
+ #include <libaio.h>
18
+ #include <sys/mman.h>
19
+ #include <sys/stat.h>
20
+ #include <sys/types.h>
21
+ #include <unistd.h>
22
+
23
+ #include <deepspeed_aio_types.h>
24
+ #include <cstring>
25
+ #include <fstream>
26
+ #include <iostream>
27
+ #include <memory>
28
+ #include <string>
29
+ #include <vector>
30
+
31
+ struct io_xfer_ctxt {
32
+ const int _fd;
33
+ const int64_t _file_base_offset;
34
+ const int64_t _buffer_base_offset;
35
+ const void* _mem_buffer;
36
+ const int64_t _num_bytes;
37
+
38
+ io_xfer_ctxt(const int fd,
39
+ const int64_t file_offset,
40
+ const int64_t buffer_offset,
41
+ const int64_t num_bytes,
42
+ const void* buffer);
43
+ };
44
+
45
+ struct io_prep_context {
46
+ const bool _read_op;
47
+ const std::unique_ptr<io_xfer_ctxt>& _xfer_ctxt;
48
+ const size_t _block_size;
49
+ const std::vector<struct iocb*>* _iocbs;
50
+
51
+ io_prep_context(const bool read_op,
52
+ const std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
53
+ const size_t block_size,
54
+ const std::vector<struct iocb*>* iocbs);
55
+
56
+ void prep_iocbs(const int n_iocbs,
57
+ const size_t num_bytes,
58
+ const void* start_buffer,
59
+ const int64_t start_offset);
60
+ };
61
+
62
+ struct io_prep_generator {
63
+ const bool _read_op;
64
+ const std::unique_ptr<io_xfer_ctxt>& _xfer_ctxt;
65
+ const size_t _block_size;
66
+
67
+ int64_t _remaining_bytes;
68
+ int64_t _num_io_blocks;
69
+ int64_t _remaining_io_blocks;
70
+ int64_t _next_iocb_index;
71
+
72
+ io_prep_generator(const bool read_op,
73
+ const std::unique_ptr<io_xfer_ctxt>& xfer_ctxt,
74
+ const size_t block_size);
75
+
76
+ int prep_iocbs(const int n_iocbs, std::vector<struct iocb*>* iocbs);
77
+ };
78
+
79
+ void* ds_page_aligned_alloc(const int64_t size, const bool lock = false);
80
+
81
+ int get_file_size(const char* filename, int64_t& size);
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.cpp ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include "deepspeed_aio_op_desc.h"
7
+
8
+ using namespace std;
9
+
10
+ io_op_desc_t::io_op_desc_t(const bool read_op,
11
+ const torch::Tensor& buffer,
12
+ const int fd,
13
+ const char* filename,
14
+ const int64_t file_num_bytes,
15
+ const int intra_op_parallelism,
16
+ const bool validate,
17
+ const int64_t file_offset)
18
+ : _read_op(read_op),
19
+ _buffer(buffer),
20
+ _fd(fd),
21
+ _filename(filename),
22
+ _file_num_bytes(file_num_bytes),
23
+ _file_offset(file_offset),
24
+ _intra_op_parallelism(intra_op_parallelism),
25
+ _num_bytes_per_thread(static_cast<int64_t>(buffer.nbytes()) / intra_op_parallelism),
26
+ _validate(validate)
27
+ {
28
+ }
29
+
30
+ char* io_op_desc_t::data_ptr() const { return (char*)_contiguous_buffer.data_ptr(); }
31
+
32
+ void io_op_desc_t::finish() {}
33
+
34
+ void io_op_desc_t::validate() {}
35
+
36
+ void io_op_desc_t::run(const int tid,
37
+ std::unique_ptr<aio_context>& aio_ctxt,
38
+ deepspeed_aio_config_t* aio_config)
39
+ {
40
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.h ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #ifndef _IO_OP_DESC_T_
7
+ #define _IO_OP_DESC_T_
8
+ #include <memory>
9
+ #include <queue>
10
+ #include "deepspeed_py_aio.h"
11
+
12
+ struct io_op_desc_t {
13
+ const bool _read_op;
14
+ torch::Tensor _buffer;
15
+ int _fd;
16
+ const std::string _filename;
17
+ const int64_t _file_num_bytes;
18
+ const int _intra_op_parallelism;
19
+ const int64_t _num_bytes_per_thread;
20
+ torch::Tensor _contiguous_buffer;
21
+ const bool _validate;
22
+ const int64_t _file_offset;
23
+
24
+ io_op_desc_t(const bool read_op,
25
+ const torch::Tensor& buffer,
26
+ const int fd,
27
+ const char* filename,
28
+ const int64_t file_num_bytes,
29
+ const int intra_op_parallelism,
30
+ const bool validate,
31
+ const int64_t file_offset);
32
+
33
+ virtual void run(const int tid,
34
+ std::unique_ptr<aio_context>& aio_ctxt,
35
+ deepspeed_aio_config_t* aio_config);
36
+
37
+ virtual char* data_ptr() const;
38
+
39
+ virtual void validate();
40
+
41
+ virtual void finish();
42
+ };
43
+ #endif // _IO_OP_DESC_T_
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.cpp ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include "deepspeed_aio_thread.h"
11
+
12
+ using namespace std;
13
+
14
+ deepspeed_aio_thread_t::deepspeed_aio_thread_t(const int tid, deepspeed_aio_config_t& aio_config)
15
+ : _tid(tid),
16
+ _aio_config(aio_config),
17
+ _aio_ctxt(new aio_context(aio_config._block_size, aio_config._queue_depth)),
18
+ _time_to_exit(false)
19
+ {
20
+ }
21
+
22
+ deepspeed_aio_thread_t::~deepspeed_aio_thread_t() {}
23
+
24
+ void deepspeed_aio_thread_t::run()
25
+ {
26
+ while (true) {
27
+ std::shared_ptr<struct io_op_desc_t> next_io_op = nullptr;
28
+
29
+ {
30
+ std::unique_lock<std::mutex> lock(_work_sync._mutex);
31
+ _work_sync._cond_var.wait(lock,
32
+ [this] { return (!_work_queue.empty() || _time_to_exit); });
33
+ if (!_work_queue.empty()) {
34
+ next_io_op = _work_queue.front();
35
+ _work_queue.pop();
36
+ }
37
+ }
38
+
39
+ if (next_io_op) {
40
+ next_io_op->run(_tid, _aio_ctxt, &_aio_config);
41
+
42
+ {
43
+ std::lock_guard<std::mutex> lock(_complete_sync._mutex);
44
+ _complete_queue.push(next_io_op);
45
+ }
46
+ _complete_sync._cond_var.notify_one();
47
+ }
48
+
49
+ if (_time_to_exit) { break; }
50
+ }
51
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.h ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <condition_variable>
11
+ #include <memory>
12
+ #include <queue>
13
+ #include "deepspeed_cpu_op.h"
14
+
15
+ struct thread_sync_t {
16
+ std::mutex _mutex;
17
+ std::condition_variable _cond_var;
18
+ };
19
+
20
+ struct deepspeed_aio_thread_t {
21
+ const int _tid;
22
+ deepspeed_aio_config_t& _aio_config;
23
+
24
+ std::unique_ptr<struct aio_context> _aio_ctxt;
25
+ std::queue<std::shared_ptr<struct io_op_desc_t>> _work_queue;
26
+ std::queue<std::shared_ptr<struct io_op_desc_t>> _complete_queue;
27
+
28
+ bool _time_to_exit;
29
+
30
+ struct thread_sync_t _work_sync;
31
+ struct thread_sync_t _complete_sync;
32
+
33
+ deepspeed_aio_thread_t(const int tid, deepspeed_aio_config_t& aio_config);
34
+
35
+ ~deepspeed_aio_thread_t();
36
+
37
+ void run();
38
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.cpp ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include "deepspeed_cpu_op.h"
7
+ #include "deepspeed_pin_tensor.h"
8
+
9
+ using namespace std;
10
+
11
+ cpu_op_desc_t::cpu_op_desc_t(
12
+ const bool read_op,
13
+ const torch::Tensor& buffer,
14
+ const std::unique_ptr<struct deepspeed_pin_tensor_t>& pinned_tensor_mgr,
15
+ const int fd,
16
+ const char* filename,
17
+ const int64_t file_num_bytes,
18
+ const int intra_op_parallelism,
19
+ const bool validate,
20
+ const int64_t file_offset)
21
+ : io_op_desc_t(read_op,
22
+ buffer,
23
+ fd,
24
+ filename,
25
+ file_num_bytes,
26
+ intra_op_parallelism,
27
+ validate,
28
+ file_offset),
29
+ _cpu_buffer(buffer),
30
+ _pinned_tensor_mgr(pinned_tensor_mgr),
31
+ _is_managed_bounce_buffer(false)
32
+ {
33
+ // Need to use CPU bounce buffer if buffer is not a page-locked DRAM memory.
34
+ _use_bounce_buffer =
35
+ !(_buffer.is_cpu() && (_buffer.is_pinned() || _pinned_tensor_mgr->is_managed(_buffer)));
36
+ if (_use_bounce_buffer) {
37
+ _alloc_bounce_buffer();
38
+ if (!_read_op) { _cpu_buffer.copy_(_buffer); }
39
+ }
40
+ _contiguous_buffer = _cpu_buffer.contiguous();
41
+ }
42
+
43
+ char* cpu_op_desc_t::data_ptr() const { return (char*)_contiguous_buffer.data_ptr(); }
44
+
45
+ void cpu_op_desc_t::finish()
46
+ {
47
+ if (_use_bounce_buffer) {
48
+ if (_read_op) {
49
+ if (_buffer.is_cuda()) {
50
+ _buffer.copy_(_cpu_buffer.to(torch::Device(torch::kCUDA, _buffer.get_device()),
51
+ /*non_blocking=*/true));
52
+ }
53
+ if (_buffer.is_xpu()) { _buffer.copy_(_cpu_buffer.to(torch::kXPU)); }
54
+ if (_buffer.is_cpu()) { _buffer.copy_(_cpu_buffer); }
55
+ #if defined(__ENABLE_CANN__)
56
+ if (torch_npu::utils::is_npu(_buffer)) {
57
+ auto device = at::Device("npu:0");
58
+ _buffer.copy_(_cpu_buffer.to(device));
59
+ }
60
+ #endif
61
+ }
62
+
63
+ _free_bounce_buffer();
64
+ }
65
+ }
66
+
67
+ void cpu_op_desc_t::validate()
68
+ {
69
+ validate_aio_operation(_read_op, _filename.c_str(), data_ptr(), _file_num_bytes);
70
+ }
71
+
72
+ void cpu_op_desc_t::run(const int tid,
73
+ std::unique_ptr<aio_context>& aio_ctxt,
74
+ deepspeed_aio_config_t* aio_config)
75
+ {
76
+ assert(tid < _intra_op_parallelism);
77
+ const auto buffer_base_offset = _num_bytes_per_thread * tid;
78
+ const auto file_base_offset = _file_offset + (_num_bytes_per_thread * tid);
79
+
80
+ std::unique_ptr<io_xfer_ctxt> xfer_ctxt(new io_xfer_ctxt(
81
+ _fd, file_base_offset, buffer_base_offset, _num_bytes_per_thread, data_ptr()));
82
+
83
+ if (aio_config->_overlap_events) {
84
+ do_aio_operation_overlap(_read_op, aio_ctxt, xfer_ctxt, aio_config, nullptr);
85
+ } else {
86
+ do_aio_operation_sequential(_read_op, aio_ctxt, xfer_ctxt, aio_config, nullptr);
87
+ }
88
+ }
89
+
90
+ void cpu_op_desc_t::_alloc_bounce_buffer()
91
+ {
92
+ auto options = torch::TensorOptions()
93
+ .dtype(_buffer.dtype())
94
+ .layout(_buffer.layout())
95
+ .device(torch::kCPU)
96
+ .requires_grad(false);
97
+
98
+ #if defined(__CUDA_ARCH__)
99
+ _cpu_buffer = torch::empty(_buffer.numel(), options).pin_memory();
100
+ #else
101
+ _is_managed_bounce_buffer = true;
102
+ _cpu_buffer = _pinned_tensor_mgr->alloc(_buffer.numel(), options);
103
+ #endif
104
+ }
105
+
106
+ void cpu_op_desc_t::_free_bounce_buffer()
107
+ {
108
+ if (_is_managed_bounce_buffer) { _pinned_tensor_mgr->free(_cpu_buffer); }
109
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.h ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include <memory>
7
+ #include <queue>
8
+ #include "deepspeed_aio_op_desc.h"
9
+
10
+ struct cpu_op_desc_t : io_op_desc_t {
11
+ torch::Tensor _cpu_buffer;
12
+ bool _use_bounce_buffer;
13
+ bool _is_managed_bounce_buffer;
14
+ const std::unique_ptr<struct deepspeed_pin_tensor_t>& _pinned_tensor_mgr;
15
+
16
+ cpu_op_desc_t(const bool read_op,
17
+ const torch::Tensor& buffer,
18
+ const std::unique_ptr<struct deepspeed_pin_tensor_t>& pinned_tensor_mgr,
19
+ const int fd,
20
+ const char* filename,
21
+ const int64_t file_num_bytes,
22
+ const int intra_op_parallelism,
23
+ const bool validate,
24
+ const int64_t file_offset);
25
+
26
+ void run(const int tid,
27
+ std::unique_ptr<aio_context>& aio_ctxt,
28
+ deepspeed_aio_config_t* aio_config);
29
+
30
+ char* data_ptr() const;
31
+
32
+ void validate();
33
+
34
+ void finish();
35
+
36
+ void _alloc_bounce_buffer();
37
+ void _free_bounce_buffer();
38
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.cpp ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for managing CPU tensors occupying page-locked memory.
8
+ */
9
+
10
+ #include "deepspeed_pin_tensor.h"
11
+
12
+ using namespace std;
13
+
14
+ deepspeed_pin_tensor_t::~deepspeed_pin_tensor_t()
15
+ {
16
+ for (auto iter = _locked_tensors.begin(); iter != _locked_tensors.end(); ++iter) {
17
+ munlock(iter->first, iter->second);
18
+ std::free((void*)iter->first);
19
+ }
20
+ _locked_tensors.clear();
21
+ }
22
+
23
+ torch::Tensor deepspeed_pin_tensor_t::alloc(const int64_t num_elem,
24
+ const torch::TensorOptions& options)
25
+ {
26
+ const auto scalar_dtype = torch::typeMetaToScalarType(options.dtype());
27
+ const auto num_bytes = num_elem * torch::elementSize(scalar_dtype);
28
+ auto pinned_buffer = ds_page_aligned_alloc(num_bytes, true);
29
+ assert(nullptr != pinned_buffer);
30
+
31
+ _locked_tensors[pinned_buffer] = num_bytes;
32
+
33
+ return at::from_blob(pinned_buffer, static_cast<int64_t>(num_elem), options);
34
+ }
35
+
36
+ torch::Tensor deepspeed_pin_tensor_t::alloc(const int64_t num_elem, const at::ScalarType& elem_type)
37
+ {
38
+ auto options = torch::TensorOptions().dtype(elem_type).device(torch::kCPU).requires_grad(false);
39
+ return alloc(num_elem, options);
40
+ }
41
+
42
+ bool deepspeed_pin_tensor_t::free(torch::Tensor& locked_tensor)
43
+ {
44
+ auto addr = locked_tensor.data_ptr();
45
+ if (_locked_tensors.find(addr) != _locked_tensors.end()) {
46
+ munlock(addr, _locked_tensors[addr]);
47
+ std::free(addr);
48
+ _locked_tensors.erase(addr);
49
+ return true;
50
+ }
51
+
52
+ return false;
53
+ }
54
+
55
+ bool deepspeed_pin_tensor_t::is_managed(const torch::Tensor& buffer)
56
+ {
57
+ if (!buffer.is_cpu()) { return false; }
58
+ auto addr = buffer.data_ptr();
59
+ if (_locked_tensors.find(addr) != _locked_tensors.end()) { return true; }
60
+ return false;
61
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.h ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for managing CPU tensors occupying page-locked memory.
8
+ TODO: Implement a full-featured manager that
9
+ 1. Avoid page-locked memory leaks
10
+ 2. Minimize page-locked memory usage by reducing internal fragmentation
11
+ Functionality for managing CPU tensors occupying page-locked memory.
12
+ */
13
+
14
+ #include <map>
15
+ #include "deepspeed_py_aio.h"
16
+
17
+ struct deepspeed_pin_tensor_t {
18
+ std::map<void*, int64_t> _locked_tensors;
19
+
20
+ deepspeed_pin_tensor_t() = default;
21
+
22
+ ~deepspeed_pin_tensor_t();
23
+
24
+ torch::Tensor alloc(const int64_t num_elem, const at::ScalarType& elem_type);
25
+ torch::Tensor alloc(const int64_t num_elem, const torch::TensorOptions& options);
26
+
27
+ bool free(torch::Tensor& locked_tensor);
28
+
29
+ bool is_managed(const torch::Tensor& buffer);
30
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.cpp ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <assert.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+
14
+ #include <fcntl.h>
15
+ #include <sys/mman.h>
16
+ #include <sys/stat.h>
17
+ #include <sys/types.h>
18
+ #include <unistd.h>
19
+
20
+ #include <cassert>
21
+ #include <chrono>
22
+ #include <cstring>
23
+ #include <fstream>
24
+ #include <iostream>
25
+ #include <memory>
26
+ #include <string>
27
+ #include <vector>
28
+
29
+ #include "deepspeed_py_aio.h"
30
+
31
+ using namespace std;
32
+ using namespace std::chrono;
33
+
34
+ #define DEBUG_DS_AIO_READ 0
35
+ #define DEBUG_DS_AIO_WRITE 0
36
+
37
+ static const std::string c_library_name = "deepspeed_aio";
38
+
39
+ int deepspeed_py_aio_write(const torch::Tensor& buffer,
40
+ const char* filename,
41
+ const int block_size,
42
+ const int queue_depth,
43
+ const bool single_submit,
44
+ const bool overlap_events,
45
+ const bool validate)
46
+ {
47
+ const auto start_time = std::chrono::high_resolution_clock::now();
48
+ deepspeed_aio_config_t config(block_size, queue_depth, single_submit, overlap_events, false);
49
+
50
+ const auto fd = open_file(filename, false);
51
+ if (fd == -1) { return -1; }
52
+
53
+ auto write_buffer = (char*)buffer.data_ptr();
54
+ const auto num_write_bytes = static_cast<int64_t>(buffer.nbytes());
55
+
56
+ std::unique_ptr<io_xfer_ctxt> xfer_ctxt(
57
+ new io_xfer_ctxt(fd, 0, 0, num_write_bytes, write_buffer));
58
+ std::unique_ptr<aio_context> aio_ctxt(new aio_context(config._block_size, config._queue_depth));
59
+
60
+ if (config._overlap_events) {
61
+ do_aio_operation_overlap(false, aio_ctxt, xfer_ctxt, &config, nullptr);
62
+ } else {
63
+ do_aio_operation_sequential(false, aio_ctxt, xfer_ctxt, &config, nullptr);
64
+ }
65
+ const std::chrono::duration<double> aio_time =
66
+ std::chrono::high_resolution_clock::now() - start_time;
67
+
68
+ close(fd);
69
+
70
+ if (validate) { validate_aio_operation(false, filename, write_buffer, num_write_bytes); }
71
+
72
+ const std::chrono::duration<double> fn_time =
73
+ std::chrono::high_resolution_clock::now() - start_time;
74
+ std::cout << "Elapsed time(usec): " << "aio = " << aio_time.count() * 1e6
75
+ << " call = " << fn_time.count() * 1e6 << std::endl;
76
+ return 0;
77
+ }
78
+
79
+ int deepspeed_py_aio_read(torch::Tensor& buffer,
80
+ const char* filename,
81
+ const int block_size,
82
+ const int queue_depth,
83
+ const bool single_submit,
84
+ const bool overlap_events,
85
+ const bool validate)
86
+ {
87
+ const auto start_time = std::chrono::high_resolution_clock::now();
88
+ int64_t num_file_bytes;
89
+ if (-1 == get_file_size(filename, num_file_bytes)) {
90
+ const auto error_code = errno;
91
+ report_file_error(filename, " fstat for read", error_code);
92
+ return -1;
93
+ }
94
+
95
+ deepspeed_aio_config_t config(block_size, queue_depth, single_submit, overlap_events, false);
96
+ const auto fd = open_file(filename, true);
97
+ if (fd == -1) { return -1; }
98
+
99
+ auto read_buffer = (char*)buffer.data_ptr();
100
+ assert(static_cast<int64_t>(buffer.nbytes()) == num_file_bytes);
101
+
102
+ std::unique_ptr<io_xfer_ctxt> xfer_ctxt(
103
+ new io_xfer_ctxt(fd, 0, 0, num_file_bytes, read_buffer));
104
+ std::unique_ptr<aio_context> aio_ctxt(new aio_context(config._block_size, config._queue_depth));
105
+
106
+ if (config._overlap_events) {
107
+ do_aio_operation_overlap(true, aio_ctxt, xfer_ctxt, &config, nullptr);
108
+ } else {
109
+ do_aio_operation_sequential(true, aio_ctxt, xfer_ctxt, &config, nullptr);
110
+ }
111
+ const std::chrono::duration<double> aio_time =
112
+ std::chrono::high_resolution_clock::now() - start_time;
113
+
114
+ close(fd);
115
+
116
+ if (validate) { validate_aio_operation(true, filename, read_buffer, num_file_bytes); }
117
+
118
+ const std::chrono::duration<double> fn_time =
119
+ std::chrono::high_resolution_clock::now() - start_time;
120
+ std::cout << "Elapsed time(usec): " << "aio = " << aio_time.count() * 1e6
121
+ << " call = " << fn_time.count() * 1e6 << std::endl;
122
+ return 0;
123
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.h ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <deepspeed_aio_common.h>
11
+ #include <stdlib.h>
12
+ #include <torch/extension.h>
13
+
14
+ int deepspeed_py_aio_write(const torch::Tensor& buffer,
15
+ const char* filename,
16
+ const int block_size,
17
+ const int queue_depth,
18
+ const bool single_submit,
19
+ const bool overlap_events,
20
+ const bool validate);
21
+
22
+ int deepspeed_py_aio_read(torch::Tensor& buffer,
23
+ const char* filename,
24
+ const int block_size,
25
+ const int queue_depth,
26
+ const bool single_submit,
27
+ const bool overlap_events,
28
+ const bool validate);
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.cpp ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include "deepspeed_py_aio_handle.h"
11
+ #include <cstdlib>
12
+
13
+ using namespace std;
14
+
15
+ deepspeed_aio_handle_t::deepspeed_aio_handle_t(const int block_size,
16
+ const int queue_depth,
17
+ const bool single_submit,
18
+ const bool overlap_events,
19
+ const int intra_op_parallelism)
20
+ : deepspeed_io_handle_t(block_size,
21
+ queue_depth,
22
+ single_submit,
23
+ overlap_events,
24
+ intra_op_parallelism)
25
+ {
26
+ }
27
+
28
+ deepspeed_aio_handle_t::~deepspeed_aio_handle_t() {}
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.h ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <condition_variable>
11
+ #include <memory>
12
+ #include "deepspeed_py_io_handle.h"
13
+
14
+ struct deepspeed_aio_handle_t : deepspeed_io_handle_t {
15
+ deepspeed_aio_handle_t(const int block_size,
16
+ const int queue_depth,
17
+ const bool single_submit,
18
+ const bool overlap_events,
19
+ const int intra_op_parallelism);
20
+
21
+ ~deepspeed_aio_handle_t();
22
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.cpp ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include "deepspeed_py_copy.h"
11
+ #include <omp.h>
12
+
13
+ #define ROUND_DOWN(size, step) ((size) & ~((step) - 1))
14
+
15
+ #if defined(__AVX512__) or defined(__AVX256__)
16
+ union AVX_Data {
17
+ #if defined(__AVX512__)
18
+ __m512 data;
19
+ #else
20
+ __m256 data;
21
+ #endif
22
+ };
23
+ #endif
24
+
25
+ static void helper_memcpy_1(float* dest, float* src, size_t param_size)
26
+ {
27
+ size_t rounded_size = 0;
28
+
29
+ #if defined(__AVX512__) or defined(__AVX256__)
30
+
31
+ rounded_size = ROUND_DOWN(param_size, SIMD_WIDTH);
32
+
33
+ for (size_t t = 0; t < rounded_size; t += TILE) {
34
+ size_t copy_size = TILE;
35
+ if ((t + TILE) > rounded_size) copy_size = rounded_size - t;
36
+ size_t offset = copy_size + t;
37
+ #pragma omp parallel for
38
+ for (size_t i = t; i < offset; i += SIMD_WIDTH) {
39
+ AVX_Data src_4;
40
+ src_4.data = SIMD_LOAD(src + i);
41
+
42
+ SIMD_STORE(dest + i, src_4.data);
43
+ }
44
+ }
45
+
46
+ #endif
47
+
48
+ if (param_size > rounded_size) {
49
+ #pragma omp parallel for
50
+ for (size_t k = rounded_size; k < param_size; k++) { dest[k] = src[k]; }
51
+ }
52
+ }
53
+
54
+ static void helper_memcpy_4(float* dest, float* src, size_t param_size)
55
+ {
56
+ size_t rounded_size = 0;
57
+
58
+ #if defined(__AVX512__) or defined(__AVX256__)
59
+
60
+ rounded_size = ROUND_DOWN(param_size, (SIMD_WIDTH << 2));
61
+
62
+ for (size_t t = 0; t < rounded_size; t += TILE) {
63
+ size_t copy_size = TILE;
64
+ if ((t + TILE) > rounded_size) copy_size = rounded_size - t;
65
+ size_t offset = copy_size + t;
66
+ #pragma omp parallel for
67
+ for (size_t i = t; i < offset; i += (SIMD_WIDTH << 2)) {
68
+ AVX_Data src_4[4];
69
+ src_4[0].data = SIMD_LOAD(src + i);
70
+ src_4[1].data = SIMD_LOAD(src + i + SIMD_WIDTH);
71
+ src_4[2].data = SIMD_LOAD(src + i + (SIMD_WIDTH << 1));
72
+ src_4[3].data = SIMD_LOAD(src + i + SIMD_WIDTH * 3);
73
+
74
+ SIMD_STORE(dest + i, src_4[0].data);
75
+ SIMD_STORE(dest + i + SIMD_WIDTH, src_4[1].data);
76
+ SIMD_STORE(dest + i + (SIMD_WIDTH << 1), src_4[2].data);
77
+ SIMD_STORE(dest + i + SIMD_WIDTH * 3, src_4[3].data);
78
+ }
79
+ }
80
+ #endif
81
+ if (param_size > rounded_size)
82
+ helper_memcpy_1((dest + rounded_size), (src + rounded_size), (param_size - rounded_size));
83
+ }
84
+
85
+ static void helper_mempcy_8(float* dest, float* src, size_t param_size)
86
+ {
87
+ size_t rounded_size = 0;
88
+
89
+ #if defined(__AVX512__) or defined(__AVX256__)
90
+
91
+ rounded_size = ROUND_DOWN(param_size, (SIMD_WIDTH << 2));
92
+
93
+ for (size_t t = 0; t < rounded_size; t += TILE) {
94
+ size_t copy_size = TILE;
95
+ if ((t + TILE) > rounded_size) copy_size = rounded_size - t;
96
+ size_t offset = copy_size + t;
97
+ #pragma omp parallel for
98
+ for (size_t i = t; i < offset; i += (SIMD_WIDTH << 3)) {
99
+ AVX_Data src_4[8];
100
+ src_4[0].data = SIMD_LOAD(src + i);
101
+ src_4[1].data = SIMD_LOAD(src + i + SIMD_WIDTH);
102
+ src_4[2].data = SIMD_LOAD(src + i + (SIMD_WIDTH << 1));
103
+ src_4[3].data = SIMD_LOAD(src + i + SIMD_WIDTH * 3);
104
+ src_4[4].data = SIMD_LOAD(src + i + (SIMD_WIDTH << 2));
105
+ src_4[5].data = SIMD_LOAD(src + i + SIMD_WIDTH * 5);
106
+ src_4[6].data = SIMD_LOAD(src + i + SIMD_WIDTH * 6);
107
+ src_4[7].data = SIMD_LOAD(src + i + SIMD_WIDTH * 7);
108
+
109
+ SIMD_STORE(dest + i, src_4[0].data);
110
+ SIMD_STORE(dest + i + SIMD_WIDTH, src_4[1].data);
111
+ SIMD_STORE(dest + i + (SIMD_WIDTH << 1), src_4[2].data);
112
+ SIMD_STORE(dest + i + SIMD_WIDTH * 3, src_4[3].data);
113
+ SIMD_STORE(dest + i + (SIMD_WIDTH << 2), src_4[4].data);
114
+ SIMD_STORE(dest + i + SIMD_WIDTH * 5, src_4[5].data);
115
+ SIMD_STORE(dest + i + SIMD_WIDTH * 6, src_4[6].data);
116
+ SIMD_STORE(dest + i + SIMD_WIDTH * 7, src_4[7].data);
117
+ }
118
+ }
119
+ #endif
120
+ if (param_size > rounded_size)
121
+ helper_memcpy_4((dest + rounded_size), (src + rounded_size), (param_size - rounded_size));
122
+ }
123
+
124
+ int deepspeed_py_memcpy(torch::Tensor& dest, const torch::Tensor& src)
125
+ {
126
+ auto dest_c = dest.contiguous();
127
+ auto src_c = src.contiguous();
128
+
129
+ float* dest_ptr = (float*)dest_c.data_ptr();
130
+ float* src_ptr = (float*)src_c.data_ptr();
131
+
132
+ helper_mempcy_8(dest_ptr, src_ptr, dest_c.size(0));
133
+
134
+ return 0;
135
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.h ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #if (__x86_64__ || __i386__)
11
+ #include <cpuid.h>
12
+ #include <x86intrin.h>
13
+ #endif
14
+
15
+ #include <deepspeed_aio_common.h>
16
+ #include <stdlib.h>
17
+ #include <torch/extension.h>
18
+
19
+ #define TILE (1024 * 1024 * 1024)
20
+
21
+ #if defined(__AVX512__)
22
+ #define SIMD_STORE(a, d) _mm512_storeu_ps(a, d)
23
+ #define SIMD_LOAD(x) _mm512_loadu_ps(x)
24
+ #define SIMD_SET(x) _mm512_set1_ps(x)
25
+ #define SIMD_MUL(x, y) _mm512_mul_ps(x, y)
26
+ #define SIMD_FMA(x, y, c) _mm512_fmadd_ps(x, y, c)
27
+ #define SIMD_SQRT(x) _mm512_sqrt_ps(x)
28
+ #define SIMD_DIV(x, y) _mm512_div_ps(x, y)
29
+ #define SIMD_WIDTH 16
30
+ #else
31
+ #if defined(__AVX256__)
32
+ #define SIMD_STORE(a, d) _mm256_storeu_ps(a, d)
33
+ #define SIMD_LOAD(x) _mm256_loadu_ps(x)
34
+ #define SIMD_SET(x) _mm256_set1_ps(x)
35
+ #define SIMD_MUL(x, y) _mm256_mul_ps(x, y)
36
+ #define SIMD_FMA(x, y, c) _mm256_fmadd_ps(x, y, c)
37
+ #define SIMD_SQRT(x) _mm256_sqrt_ps(x)
38
+ #define SIMD_DIV(x, y) _mm256_div_ps(x, y)
39
+ #define SIMD_WIDTH 8
40
+ #endif
41
+ #endif
42
+
43
+ int deepspeed_py_memcpy(torch::Tensor& dest, const torch::Tensor& src);
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.cpp ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include "deepspeed_py_io_handle.h"
11
+ #include <cstdlib>
12
+
13
+ using namespace std;
14
+
15
+ static void _start_aio_thread(std::shared_ptr<struct deepspeed_aio_thread_t> ctxt) { ctxt->run(); }
16
+
17
+ deepspeed_io_handle_t::deepspeed_io_handle_t(const int block_size,
18
+ const int queue_depth,
19
+ const bool single_submit,
20
+ const bool overlap_events,
21
+ const int intra_op_parallelism)
22
+ : _aio_ctxt(new aio_context(block_size, queue_depth)),
23
+ _single_submit(single_submit),
24
+ _overlap_events(overlap_events),
25
+ _intra_op_parallelism(intra_op_parallelism),
26
+ _aio_config(block_size, queue_depth, single_submit, overlap_events, false),
27
+ _num_pending_ops(0),
28
+ _pinned_tensor_mgr(new deepspeed_pin_tensor_t())
29
+ {
30
+ for (auto i = 0; i < intra_op_parallelism; ++i) {
31
+ _thread_contexts.push_back(std::make_shared<deepspeed_aio_thread_t>(i, _aio_config));
32
+ }
33
+
34
+ for (auto& ctxt : _thread_contexts) {
35
+ _threads.push_back(std::thread(_start_aio_thread, ctxt));
36
+ }
37
+ }
38
+
39
+ deepspeed_io_handle_t::~deepspeed_io_handle_t()
40
+ {
41
+ _stop_threads();
42
+ for (auto& thr : _threads) { thr.join(); }
43
+ }
44
+
45
+ const int deepspeed_io_handle_t::get_block_size() const
46
+ {
47
+ return _aio_ctxt ? _aio_ctxt->_block_size : -1;
48
+ }
49
+
50
+ const int deepspeed_io_handle_t::get_queue_depth() const
51
+ {
52
+ return _aio_ctxt ? _aio_ctxt->_queue_depth : -1;
53
+ }
54
+
55
+ const bool deepspeed_io_handle_t::get_single_submit() const { return _single_submit; }
56
+
57
+ const bool deepspeed_io_handle_t::get_overlap_events() const { return _overlap_events; }
58
+
59
+ const int deepspeed_io_handle_t::get_intra_op_parallelism() const { return _intra_op_parallelism; }
60
+
61
+ int deepspeed_io_handle_t::read(torch::Tensor& buffer,
62
+ const char* filename,
63
+ const bool validate,
64
+ const int64_t file_offset)
65
+ {
66
+ const auto start_time = std::chrono::high_resolution_clock::now();
67
+
68
+ assert(_aio_ctxt);
69
+
70
+ int64_t num_file_bytes;
71
+ if (-1 == get_file_size(filename, num_file_bytes)) {
72
+ const auto error_code = errno;
73
+ report_file_error(filename, " fstat for read", error_code);
74
+ return -1;
75
+ }
76
+ assert(static_cast<int64_t>(buffer.nbytes()) == num_file_bytes);
77
+
78
+ const auto fd = open_file(filename, true);
79
+ if (fd == -1) { return -1; }
80
+
81
+ auto read_buffer = (char*)buffer.data_ptr();
82
+ std::unique_ptr<io_xfer_ctxt> xfer_ctxt(
83
+ new io_xfer_ctxt(fd, file_offset, 0, num_file_bytes, read_buffer));
84
+
85
+ if (_aio_config._overlap_events) {
86
+ do_aio_operation_overlap(true, _aio_ctxt, xfer_ctxt, &_aio_config, nullptr);
87
+ } else {
88
+ do_aio_operation_sequential(true, _aio_ctxt, xfer_ctxt, &_aio_config, nullptr);
89
+ }
90
+
91
+ close(fd);
92
+ const std::chrono::duration<double> aio_time =
93
+ std::chrono::high_resolution_clock::now() - start_time;
94
+
95
+ if (validate) { validate_aio_operation(true, filename, read_buffer, num_file_bytes); }
96
+ const std::chrono::duration<double> fn_time =
97
+ std::chrono::high_resolution_clock::now() - start_time;
98
+ std::cout << "Elapsed time(usec): " << "aio = " << aio_time.count() * 1e6
99
+ << " call = " << fn_time.count() * 1e6 << std::endl;
100
+ return 0;
101
+ }
102
+
103
+ int deepspeed_io_handle_t::write(const torch::Tensor& buffer,
104
+ const char* filename,
105
+ const bool validate,
106
+ const int64_t file_offset)
107
+ {
108
+ assert(_aio_ctxt);
109
+
110
+ const auto start_time = std::chrono::high_resolution_clock::now();
111
+
112
+ const auto fd = open_file(filename, false);
113
+ if (fd == -1) { return -1; }
114
+
115
+ auto write_buffer = (char*)buffer.data_ptr();
116
+ const auto num_write_bytes = static_cast<int64_t>(buffer.nbytes());
117
+ std::unique_ptr<io_xfer_ctxt> xfer_ctxt(
118
+ new io_xfer_ctxt(fd, file_offset, 0, num_write_bytes, write_buffer));
119
+
120
+ if (_aio_config._overlap_events) {
121
+ do_aio_operation_overlap(false, _aio_ctxt, xfer_ctxt, &_aio_config, nullptr);
122
+ } else {
123
+ do_aio_operation_sequential(false, _aio_ctxt, xfer_ctxt, &_aio_config, nullptr);
124
+ }
125
+ const std::chrono::duration<double> aio_time =
126
+ std::chrono::high_resolution_clock::now() - start_time;
127
+
128
+ close(fd);
129
+
130
+ if (validate) { validate_aio_operation(false, filename, write_buffer, num_write_bytes); }
131
+
132
+ const std::chrono::duration<double> fn_time =
133
+ std::chrono::high_resolution_clock::now() - start_time;
134
+ std::cout << "Elapsed time(usec): " << "aio = " << aio_time.count() * 1e6
135
+ << " call = " << fn_time.count() * 1e6 << std::endl;
136
+ return 0;
137
+ }
138
+
139
+ void deepspeed_io_handle_t::_schedule_aio_work(std::shared_ptr<struct io_op_desc_t> scheduled_op)
140
+ {
141
+ for (auto& ctxt : _thread_contexts) {
142
+ {
143
+ std::lock_guard<std::mutex> lock(ctxt->_work_sync._mutex);
144
+ ctxt->_work_queue.push(scheduled_op);
145
+ }
146
+ ctxt->_work_sync._cond_var.notify_one();
147
+ }
148
+ _num_pending_ops++;
149
+ }
150
+
151
+ std::shared_ptr<struct io_op_desc_t> deepspeed_io_handle_t::_wait_for_aio_work()
152
+ {
153
+ std::shared_ptr<struct io_op_desc_t> completed_op = nullptr;
154
+ for (auto& ctxt : _thread_contexts) {
155
+ std::unique_lock<std::mutex> lock(ctxt->_complete_sync._mutex);
156
+ ctxt->_complete_sync._cond_var.wait(lock,
157
+ [ctxt] { return !ctxt->_complete_queue.empty(); });
158
+ completed_op = ctxt->_complete_queue.front();
159
+ ctxt->_complete_queue.pop();
160
+ }
161
+ return completed_op;
162
+ }
163
+
164
+ void deepspeed_io_handle_t::_stop_threads()
165
+ {
166
+ assert(0 == _num_pending_ops);
167
+ for (auto& ctxt : _thread_contexts) {
168
+ {
169
+ std::lock_guard<std::mutex> lock(ctxt->_work_sync._mutex);
170
+ ctxt->_time_to_exit = true;
171
+ }
172
+ ctxt->_work_sync._cond_var.notify_one();
173
+ }
174
+ }
175
+
176
+ int deepspeed_io_handle_t::wait()
177
+ {
178
+ assert(_num_pending_ops > 0);
179
+ auto num_completed_ops = 0;
180
+
181
+ while (_num_pending_ops > 0) {
182
+ auto completed_op = _wait_for_aio_work();
183
+
184
+ if (completed_op->_validate) { completed_op->validate(); }
185
+
186
+ completed_op->finish();
187
+
188
+ close(completed_op->_fd);
189
+
190
+ --_num_pending_ops;
191
+ ++num_completed_ops;
192
+ }
193
+
194
+ return num_completed_ops;
195
+ }
196
+
197
+ bool deepspeed_io_handle_t::_is_valid_parallel_aio_op(const bool read_op, const int64_t num_bytes)
198
+ {
199
+ const auto op_string = read_op ? "Read" : "Write";
200
+ if (num_bytes % get_intra_op_parallelism()) {
201
+ std::cout << "deepspeed_aio failure: parallel " << op_string << " num_bytes = " << num_bytes
202
+ << " not divisible by thread count = " << get_intra_op_parallelism() << std::endl;
203
+ return false;
204
+ }
205
+
206
+ return true;
207
+ }
208
+
209
+ std::shared_ptr<struct io_op_desc_t> deepspeed_io_handle_t::_create_io_op_desc(
210
+ const bool read_op,
211
+ const torch::Tensor& buffer,
212
+ const int fd,
213
+ const char* filename,
214
+ const int64_t file_num_bytes,
215
+ const bool validate,
216
+ const int64_t file_offset)
217
+ {
218
+ return std::make_shared<cpu_op_desc_t>(read_op,
219
+ buffer,
220
+ _pinned_tensor_mgr,
221
+ fd,
222
+ filename,
223
+ file_num_bytes,
224
+ _intra_op_parallelism,
225
+ validate,
226
+ file_offset);
227
+ }
228
+
229
+ int deepspeed_io_handle_t::pread(const torch::Tensor& buffer,
230
+ const char* filename,
231
+ const bool validate,
232
+ const bool async,
233
+ const int64_t file_offset)
234
+ {
235
+ int64_t num_file_bytes;
236
+ if (-1 == get_file_size(filename, num_file_bytes)) {
237
+ const auto error_code = errno;
238
+ report_file_error(filename, " fstat for read", error_code);
239
+ return -1;
240
+ }
241
+
242
+ // buffer can exceed file size to enable 4k alignment
243
+ const auto buffer_bytes = static_cast<int64_t>(buffer.nbytes());
244
+ assert((num_file_bytes % _intra_op_parallelism) == 0);
245
+
246
+ if (!_is_valid_parallel_aio_op(true, buffer_bytes)) { return -1; }
247
+
248
+ const auto fd = open_file(filename, true);
249
+ if (fd == -1) { return -1; }
250
+
251
+ auto scheduled_op =
252
+ _create_io_op_desc(true, buffer, fd, filename, num_file_bytes, validate, file_offset);
253
+
254
+ _schedule_aio_work(scheduled_op);
255
+
256
+ if (async) { return 0; }
257
+
258
+ return wait();
259
+ }
260
+
261
+ int deepspeed_io_handle_t::pwrite(const torch::Tensor& buffer,
262
+ const char* filename,
263
+ const bool validate,
264
+ const bool async,
265
+ const int64_t file_offset)
266
+ {
267
+ const auto num_write_bytes = static_cast<int64_t>(buffer.nbytes());
268
+ assert((num_write_bytes % _intra_op_parallelism) == 0);
269
+
270
+ if (!_is_valid_parallel_aio_op(false, num_write_bytes)) { return -1; }
271
+
272
+ const auto fd = open_file(filename, false);
273
+ if (fd == -1) { return -1; }
274
+
275
+ auto scheduled_op =
276
+ _create_io_op_desc(false, buffer, fd, filename, num_write_bytes, validate, file_offset);
277
+
278
+ _schedule_aio_work(scheduled_op);
279
+
280
+ if (async) { return 0; }
281
+
282
+ return wait();
283
+ }
284
+
285
+ int deepspeed_io_handle_t::sync_pread(torch::Tensor& buffer,
286
+ const char* filename,
287
+ const int64_t file_offset)
288
+ {
289
+ return pread(buffer, filename, false, false, file_offset);
290
+ }
291
+
292
+ int deepspeed_io_handle_t::sync_pwrite(const torch::Tensor& buffer,
293
+ const char* filename,
294
+ const int64_t file_offset)
295
+ {
296
+ return pwrite(buffer, filename, false, false, file_offset);
297
+ }
298
+
299
+ int deepspeed_io_handle_t::async_pread(torch::Tensor& buffer,
300
+ const char* filename,
301
+ const int64_t file_offset)
302
+ {
303
+ return pread(buffer, filename, false, true, file_offset);
304
+ }
305
+
306
+ int deepspeed_io_handle_t::async_pwrite(const torch::Tensor& buffer,
307
+ const char* filename,
308
+ const int64_t file_offset)
309
+ {
310
+ return pwrite(buffer, filename, false, true, file_offset);
311
+ }
312
+
313
+ at::Tensor deepspeed_io_handle_t::new_cpu_locked_tensor(const int64_t num_elem,
314
+ const torch::Tensor& example_tensor)
315
+ {
316
+ return _pinned_tensor_mgr->alloc(num_elem, example_tensor.scalar_type());
317
+ }
318
+
319
+ bool deepspeed_io_handle_t::free_cpu_locked_tensor(torch::Tensor& locked_tensor)
320
+ {
321
+ return _pinned_tensor_mgr->free(locked_tensor);
322
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.h ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <condition_variable>
11
+ #include <memory>
12
+ #include "deepspeed_aio_thread.h"
13
+ #include "deepspeed_pin_tensor.h"
14
+
15
+ struct deepspeed_io_handle_t {
16
+ std::unique_ptr<struct aio_context> _aio_ctxt;
17
+ const bool _single_submit;
18
+ const bool _overlap_events;
19
+ const int _intra_op_parallelism;
20
+ deepspeed_aio_config_t _aio_config;
21
+
22
+ std::vector<std::shared_ptr<struct deepspeed_aio_thread_t>> _thread_contexts;
23
+ std::vector<std::thread> _threads;
24
+ int _num_pending_ops;
25
+ std::unique_ptr<struct deepspeed_pin_tensor_t> _pinned_tensor_mgr;
26
+
27
+ deepspeed_io_handle_t(const int block_size,
28
+ const int queue_depth,
29
+ const bool single_submit,
30
+ const bool overlap_events,
31
+ const int intra_op_parallelism);
32
+
33
+ virtual ~deepspeed_io_handle_t() = 0;
34
+
35
+ const int get_block_size() const;
36
+ const int get_queue_depth() const;
37
+ const bool get_single_submit() const;
38
+ const bool get_overlap_events() const;
39
+ const int get_intra_op_parallelism() const;
40
+
41
+ int read(torch::Tensor& buffer,
42
+ const char* filename,
43
+ const bool validate,
44
+ const int64_t file_offset);
45
+
46
+ int write(const torch::Tensor& buffer,
47
+ const char* filename,
48
+ const bool validate,
49
+ const int64_t file_offset);
50
+
51
+ int pread(const torch::Tensor& buffer,
52
+ const char* filename,
53
+ const bool validate,
54
+ const bool async,
55
+ const int64_t file_offset);
56
+
57
+ int pwrite(const torch::Tensor& buffer,
58
+ const char* filename,
59
+ const bool validate,
60
+ const bool async,
61
+ const int64_t file_offset);
62
+
63
+ int sync_pread(torch::Tensor& buffer, const char* filename, const int64_t file_offset);
64
+
65
+ int sync_pwrite(const torch::Tensor& buffer, const char* filename, const int64_t file_offset);
66
+
67
+ int async_pread(torch::Tensor& buffer, const char* filename, const int64_t file_offset);
68
+
69
+ int async_pwrite(const torch::Tensor& buffer, const char* filename, const int64_t file_offset);
70
+
71
+ // TODO: Make API's args to be shape and dtype.
72
+ torch::Tensor new_cpu_locked_tensor(const int64_t num_elem,
73
+ const torch::Tensor& example_tensor);
74
+
75
+ bool free_cpu_locked_tensor(torch::Tensor&);
76
+
77
+ int wait();
78
+
79
+ void _stop_threads();
80
+
81
+ void _schedule_aio_work(std::shared_ptr<struct io_op_desc_t> scheduled_op);
82
+
83
+ std::shared_ptr<struct io_op_desc_t> _wait_for_aio_work();
84
+
85
+ bool _is_valid_parallel_aio_op(const bool read_op, const int64_t num_bytes);
86
+
87
+ virtual std::shared_ptr<struct io_op_desc_t> _create_io_op_desc(const bool read_op,
88
+ const torch::Tensor& buffer,
89
+ const int fd,
90
+ const char* filename,
91
+ const int64_t file_num_bytes,
92
+ const bool validate,
93
+ const int64_t file_offset);
94
+ };
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/py_ds_aio.cpp ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ /*
7
+ Functionality for swapping optimizer tensors to/from (NVMe) storage devices.
8
+ */
9
+
10
+ #include <torch/extension.h>
11
+ #include "deepspeed_py_aio_handle.h"
12
+ #include "deepspeed_py_copy.h"
13
+ using namespace pybind11::literals;
14
+
15
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
16
+ {
17
+ m.def("aio_read", &deepspeed_py_aio_read, "DeepSpeed Asynchronous I/O Read");
18
+
19
+ m.def("aio_write", &deepspeed_py_aio_write, "DeepSpeed Asynchronous I/O Write");
20
+
21
+ m.def("deepspeed_memcpy", &deepspeed_py_memcpy, "DeepSpeed Memory Copy");
22
+
23
+ py::class_<deepspeed_aio_handle_t>(m, "aio_handle")
24
+ .def(py::init<const int, const int, const bool, const bool, const int>(),
25
+ "AIO handle constructor",
26
+ "block_size"_a = 1024 * 1024,
27
+ "queue_depth"_a = 128,
28
+ "single_submit"_a = false,
29
+ "overlap_events"_a = false,
30
+ "intra_op_parallelism"_a = 1)
31
+
32
+ .def("get_block_size", &deepspeed_aio_handle_t::get_block_size)
33
+ .def("get_queue_depth", &deepspeed_aio_handle_t::get_queue_depth)
34
+ .def("get_single_submit", &deepspeed_aio_handle_t::get_single_submit)
35
+ .def("get_overlap_events", &deepspeed_aio_handle_t::get_overlap_events)
36
+ .def("get_intra_op_parallelism", &deepspeed_aio_handle_t::get_intra_op_parallelism)
37
+
38
+ .def("read",
39
+ &deepspeed_aio_handle_t::read,
40
+ "Synchronous and non-parallel file read. Returns count of completed read ops",
41
+ "buffer"_a,
42
+ "filename"_a,
43
+ "validate"_a,
44
+ "file_offset"_a = 0)
45
+
46
+ .def("write",
47
+ &deepspeed_aio_handle_t::write,
48
+ "Synchronous and non-parallel file write. Returns count of completed write ops",
49
+ "buffer"_a,
50
+ "filename"_a,
51
+ "validate"_a,
52
+ "file_offset"_a = 0)
53
+
54
+ .def("pread",
55
+ &deepspeed_aio_handle_t::pread,
56
+ "Parallel file read with option of parallelism. Returns count of completed read ops",
57
+ "buffer"_a,
58
+ "filename"_a,
59
+ "validate"_a,
60
+ "async"_a,
61
+ "file_offset"_a = 0)
62
+
63
+ .def("pwrite",
64
+ &deepspeed_aio_handle_t::pwrite,
65
+ "Parallel file write with option of parallelism. Returns count of completed write ops",
66
+ "buffer"_a,
67
+ "filename"_a,
68
+ "validate"_a,
69
+ "async"_a,
70
+ "file_offset"_a = 0)
71
+
72
+ .def("sync_pread",
73
+ &deepspeed_aio_handle_t::sync_pread,
74
+ "Synchrononous parallel file read. Returns count of completed read ops",
75
+ "buffer"_a,
76
+ "filename"_a,
77
+ "file_offset"_a = 0)
78
+
79
+ .def("sync_pwrite",
80
+ &deepspeed_aio_handle_t::sync_pwrite,
81
+ "Synchronous parallel file write. Returns count of completed write ops",
82
+ "buffer"_a,
83
+ "filename"_a,
84
+ "file_offset"_a = 0)
85
+
86
+ .def("async_pread",
87
+ &deepspeed_aio_handle_t::async_pread,
88
+ "Asynchronous parallel file read. Returns 0 on success. Returns 0 on success, and "
89
+ "following wait() returns count of completed ops.",
90
+ "buffer"_a,
91
+ "filename"_a,
92
+ "file_offset"_a = 0)
93
+
94
+ .def("async_pwrite",
95
+ &deepspeed_aio_handle_t::async_pwrite,
96
+ "Asynchronous parallel file write. Returns 0 on success, and following wait() returns "
97
+ "count of completed ops.",
98
+ "buffer"_a,
99
+ "filename"_a,
100
+ "file_offset"_a = 0)
101
+
102
+ .def("new_cpu_locked_tensor",
103
+ &deepspeed_aio_handle_t::new_cpu_locked_tensor,
104
+ "Allocate pinned CPU tensor.",
105
+ "num_elem"_a,
106
+ "example_tenosr"_a)
107
+
108
+ .def("free_cpu_locked_tensor",
109
+ &deepspeed_aio_handle_t::free_cpu_locked_tensor,
110
+ "Free pinned CPU tensor.",
111
+ "tensor"_a)
112
+
113
+ .def("wait",
114
+ &deepspeed_aio_handle_t::wait,
115
+ "Wait for (ongoing) asynchronous operations to complete");
116
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_test/single_process_config.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "block_size": [
3
+ "128K",
4
+ "256K",
5
+ "1M"
6
+ ],
7
+ "queue_depth": [
8
+ 4,
9
+ 16,
10
+ 32
11
+ ],
12
+ "io_parallel": [
13
+ 1,
14
+ 2,
15
+ 4,
16
+ 8
17
+ ],
18
+ "single_submit": [
19
+ true,
20
+ false
21
+ ],
22
+ "overlap_events": [
23
+ true,
24
+ false
25
+ ],
26
+ "threads": [
27
+ 1
28
+ ]
29
+ }
lib/python3.12/site-packages/deepspeed/ops/csrc/compile/deepcompile.cpp ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // DeepSpeed Team
5
+
6
+ #include "deepcompile.h"
7
+
8
+ #define USE_C10D_NCCL
9
+
10
+ namespace dc {
11
+
12
+ std::shared_ptr<DSParamRegistry> param_registry;
13
+ std::unordered_map<long, std::shared_ptr<CustomOpExecutor>> executors;
14
+ std::shared_ptr<DoubleBufferedReduceBucket> reduce_buckets = nullptr;
15
+
16
+ c10::intrusive_ptr<c10d::ProcessGroup> process_group = nullptr;
17
+ c10::intrusive_ptr<c10d::symmetric_memory::SymmetricMemory> symm_mem = nullptr;
18
+ ncclComm_t nccl_comm;
19
+ bool use_symm_mem;
20
+ bool clone_custom_op_output;
21
+ bool profile = false;
22
+ bool pre_div_reduce = true;
23
+
24
+ bool sync_before_reduce; // for debugging
25
+ bool sync_after_reduce; // for debugging
26
+ bool sync_before_allgather; // for debugging
27
+ bool sync_after_allgather; // for debugging
28
+
29
+ std::vector<int64_t> sizes_to_int_vector(at::IntArrayRef sizes)
30
+ {
31
+ std::vector<int64_t> result;
32
+ for (int i = 0; i < sizes.size(); i++) { result.push_back(sizes[i]); }
33
+ return result;
34
+ }
35
+
36
+ void enable_profiling(bool enable) { profile = enable; }
37
+
38
+ bool is_profiling() { return profile; }
39
+
40
+ c10::intrusive_ptr<c10d::symmetric_memory::SymmetricMemory> getSymmMemWorkspace(int64_t size)
41
+ {
42
+ c10::Device device = c10::Device(c10::kCUDA, c10::cuda::current_device());
43
+ std::vector<int64_t> sizes = {size};
44
+ std::vector<int64_t> strides = {1};
45
+ at::Tensor sym_mem_ws = c10d::symmetric_memory::empty_strided_p2p(
46
+ {size}, {1}, c10::ScalarType::Byte, device, process_group->getGroupName(), std::nullopt);
47
+ return c10d::symmetric_memory::rendezvous(sym_mem_ws);
48
+ }
49
+
50
+ void lazy_init_symm_memory()
51
+ {
52
+ if (use_symm_mem && !symm_mem) {
53
+ int64_t max_param_size = 0;
54
+ for (const auto& it : param_registry->getParams()) {
55
+ int64_t size = it.second.getDSTensor().numel() * it.second.getDSTensor().element_size();
56
+ if (size > max_param_size) { max_param_size = size; }
57
+ }
58
+ symm_mem = getSymmMemWorkspace(max_param_size);
59
+ }
60
+ }
61
+
62
+ ncclDataType_t get_nccl_data_type(at::ScalarType scalar_type)
63
+ {
64
+ switch (scalar_type) {
65
+ case at::kFloat: return ncclFloat;
66
+ case at::kHalf: return ncclHalf;
67
+ case at::kDouble: return ncclDouble;
68
+ case at::kBFloat16: return ncclBfloat16;
69
+ case at::kLong: return ncclInt64;
70
+ case at::kInt: return ncclInt;
71
+ case at::kChar: return ncclInt8;
72
+ default: throw std::runtime_error("Unsupported scalar type");
73
+ }
74
+ }
75
+
76
+ void reset()
77
+ {
78
+ executors.clear();
79
+ // We keep the buckets for memory estimation
80
+ // reduce_buckets->clear();
81
+ }
82
+
83
+ void cleanup()
84
+ {
85
+ reset();
86
+
87
+ ncclCommDestroy(nccl_comm);
88
+ process_group = nullptr;
89
+ symm_mem = nullptr;
90
+ }
91
+
92
+ at::Tensor reduce_grad(at::Tensor grad_tensor, long graph_id, long ds_id)
93
+ {
94
+ if (sync_before_reduce) { c10::cuda::device_synchronize(); }
95
+
96
+ assert(hasKey(executors, graph_id));
97
+ if (!profile) { executors[graph_id]->reduceGrad(grad_tensor, ds_id); }
98
+
99
+ if (sync_after_reduce) { c10::cuda::device_synchronize(); }
100
+
101
+ return at::Tensor();
102
+ }
103
+
104
+ at::Tensor reduce_grad_meta(at::Tensor grad_tensor, long graph_id, long ds_id)
105
+ {
106
+ return at::Tensor();
107
+ }
108
+
109
+ void free_tensors(std::vector<at::Tensor> tensors)
110
+ {
111
+ int64_t THRESHOLD = 10 * 1024 * 1024;
112
+
113
+ if (!profile) {
114
+ for (auto& tensor : tensors) {
115
+ if (tensor.is_cuda() && tensor.numel() > THRESHOLD) {
116
+ tensor.record_stream(at::cuda::getCurrentCUDAStream());
117
+ tensor.set_data(torch::empty({0}, tensor.options()));
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ void free_tensors_meta(std::vector<at::Tensor> tensors) {}
124
+
125
+ void init(c10::intrusive_ptr<c10d::ProcessGroup> pg,
126
+ int64_t initial_reduce_bucket_size,
127
+ bool enable_double_buffer,
128
+ bool _use_symm_mem,
129
+ bool _clone_custom_op_output,
130
+ bool _sync_before_reduce,
131
+ bool _sync_after_reduce,
132
+ bool _sync_before_allgather,
133
+ bool _sync_after_allgather)
134
+ {
135
+ process_group = pg;
136
+
137
+ ncclUniqueId ncclID;
138
+ ncclGetUniqueId(&ncclID);
139
+
140
+ // ProcessGroup doesn't have an API to get the CUDA stream for comm calls.
141
+ // So we create a NCCL communicator and call NCCL APIs directly.
142
+ auto vec = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&ncclID),
143
+ reinterpret_cast<uint8_t*>(&ncclID) + NCCL_UNIQUE_ID_BYTES);
144
+ auto device = torch::Device(torch::kCUDA);
145
+ at::Tensor tensor = torch::from_blob(vec.data(), {static_cast<long>(vec.size())}, torch::kUInt8)
146
+ .to(torch::Device(torch::kCUDA));
147
+ std::vector<at::Tensor> bcast_input = {tensor};
148
+
149
+ process_group->broadcast(bcast_input, c10d::BroadcastOptions())->wait();
150
+
151
+ // create a new nccl communicator
152
+ std::memcpy(&ncclID, tensor.to(torch::Device(torch::kCPU)).data_ptr(), NCCL_UNIQUE_ID_BYTES);
153
+ ncclCommInitRank(&nccl_comm, process_group->getSize(), ncclID, process_group->getRank());
154
+
155
+ param_registry = std::make_shared<DSParamRegistry>();
156
+ reduce_buckets = std::make_shared<DoubleBufferedReduceBucket>(initial_reduce_bucket_size,
157
+ enable_double_buffer);
158
+ use_symm_mem = _use_symm_mem;
159
+ clone_custom_op_output = _clone_custom_op_output;
160
+
161
+ sync_before_reduce = _sync_before_reduce;
162
+ sync_after_reduce = _sync_after_reduce;
163
+ sync_before_allgather = _sync_before_allgather;
164
+ sync_after_allgather = _sync_after_allgather;
165
+ }
166
+
167
+ void start_forward()
168
+ {
169
+ lazy_init_symm_memory();
170
+ for (auto& it : executors) { it.second->startForward(); }
171
+ }
172
+
173
+ void end_forward()
174
+ {
175
+ for (auto& it : executors) { it.second->endForward(); }
176
+ }
177
+
178
+ void start_backward(bool update)
179
+ {
180
+ for (auto& it : executors) { it.second->startBackward(update); }
181
+ }
182
+
183
+ // We don't call this
184
+ // void end_backward(bool update)
185
+ // {
186
+ // }
187
+
188
+ } // namespace dc