Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/deepspeed/compression/__init__.py +8 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/basic_layer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/compress.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/constants.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/helper.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/scheduler.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/__pycache__/utils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/compression/basic_layer.py +840 -0
- lib/python3.12/site-packages/deepspeed/compression/compress.py +239 -0
- lib/python3.12/site-packages/deepspeed/compression/config.py +452 -0
- lib/python3.12/site-packages/deepspeed/compression/constants.py +188 -0
- lib/python3.12/site-packages/deepspeed/compression/helper.py +322 -0
- lib/python3.12/site-packages/deepspeed/compression/scheduler.py +173 -0
- lib/python3.12/site-packages/deepspeed/compression/utils.py +222 -0
- lib/python3.12/site-packages/deepspeed/ops/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/ops/compile/__init__.py +6 -0
- lib/python3.12/site-packages/deepspeed/ops/compile/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adagrad/cpu_adagrad.cpp +215 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam.cpp +13 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adam/cpu_adam_impl.cpp +244 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adam/fused_adam_frontend.cpp +25 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_adam.cu +203 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/adam/multi_tensor_apply.cuh +132 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.cpp +341 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_common.h +38 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.cpp +76 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_types.h +59 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.cpp +132 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/common/deepspeed_aio_utils.h +81 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.cpp +40 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_op_desc.h +43 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.cpp +51 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_aio_thread.h +38 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.cpp +109 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_cpu_op.h +38 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.cpp +61 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_pin_tensor.h +30 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.cpp +123 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio.h +28 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.cpp +28 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_aio_handle.h +22 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.cpp +135 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_copy.h +43 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.cpp +322 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/deepspeed_py_io_handle.h +94 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_lib/py_ds_aio.cpp +116 -0
- lib/python3.12/site-packages/deepspeed/ops/csrc/aio/py_test/single_process_config.json +29 -0
- 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
|