From 58241b1d71faa262d22eff80aa8c3979dbf5e665 Mon Sep 17 00:00:00 2001 From: Heyang Qin Date: Mon, 29 Jul 2024 14:18:50 -0700 Subject: [PATCH 01/14] fix: handle exception when loading cache file in test_inference.py (#5802) This PR is to fix CI failures such as https://github.com/microsoft/DeepSpeed/actions/runs/10085903860/job/27887546470#step:8:3616 cc @tjruwase Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- tests/unit/inference/test_inference.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/inference/test_inference.py b/tests/unit/inference/test_inference.py index 76eb88eea560..eadf670d9328 100644 --- a/tests/unit/inference/test_inference.py +++ b/tests/unit/inference/test_inference.py @@ -94,7 +94,10 @@ def _hf_model_list() -> List[ModelInfo]: model_data = {"cache_time": 0, "model_list": []} if os.path.isfile(cache_file_path): with open(cache_file_path, 'rb') as f: - model_data = pickle.load(f) + try: + model_data = pickle.load(f) + except Exception as e: + print(f"Error loading cache file {cache_file_path}: {e}") current_time = time.time() From 5e8a27ad6db4a6560268431c6d19e2ec54beb0a3 Mon Sep 17 00:00:00 2001 From: Logan Adams <114770087+loadams@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:24:21 -0700 Subject: [PATCH 02/14] Pin transformers version for MII tests (#5807) Corresponding PR to https://github.com/microsoft/DeepSpeed-MII/pull/510 that is made due to changes from transformers introduced in https://github.com/huggingface/transformers/pull/31747 --- .github/workflows/nv-mii.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nv-mii.yml b/.github/workflows/nv-mii.yml index ba655f5c37e3..d394b7e24bd6 100644 --- a/.github/workflows/nv-mii.yml +++ b/.github/workflows/nv-mii.yml @@ -46,7 +46,7 @@ jobs: git clone https://github.com/huggingface/transformers cd transformers # if needed switch to the last known good SHA until transformers@master is fixed - # git checkout bdf36dc + git checkout v4.42.4 git rev-parse --short HEAD pip install . From 08598dbb3a8a8856033d7c17236902eb72cd3523 Mon Sep 17 00:00:00 2001 From: keshavkowshik Date: Wed, 31 Jul 2024 10:37:52 -0700 Subject: [PATCH 03/14] Fix op_builder for CUDA 12.5 (#5806) std lib needed to be updated to C++ version 20 for CUDA 12.5 to fix compilation issues in the op_builder. TODO: Fix may need to be extended to CUDA 12.4, needs testing. --------- Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> Co-authored-by: Lev Kurilenko --- op_builder/builder.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/op_builder/builder.py b/op_builder/builder.py index 03611bf56284..458a43c3ce5c 100644 --- a/op_builder/builder.py +++ b/op_builder/builder.py @@ -749,11 +749,18 @@ def nvcc_args(self): except ValueError: nvcc_threads = min(os.cpu_count(), 8) - cuda_major, _ = installed_cuda_version() + cuda_major, cuda_minor = installed_cuda_version() + if cuda_major > 10: + if cuda_major == 12 and cuda_minor >= 5: + std_lib = '-std=c++20' + else: + std_lib = '-std=c++17' + else: + std_lib = '-std=c++14' args += [ - '-allow-unsupported-compiler' if sys.platform == "win32" else '', '--use_fast_math', - '-std=c++17' if cuda_major > 10 else '-std=c++14', '-U__CUDA_NO_HALF_OPERATORS__', - '-U__CUDA_NO_HALF_CONVERSIONS__', '-U__CUDA_NO_HALF2_OPERATORS__', f'--threads={nvcc_threads}' + '-allow-unsupported-compiler' if sys.platform == "win32" else '', '--use_fast_math', std_lib, + '-U__CUDA_NO_HALF_OPERATORS__', '-U__CUDA_NO_HALF_CONVERSIONS__', '-U__CUDA_NO_HALF2_OPERATORS__', + f'--threads={nvcc_threads}' ] if os.environ.get('DS_DEBUG_CUDA_BUILD', '0') == '1': args.append('--ptxas-options=-v') From 550f9c75bcb586e8f0d1523a36c81a10a2fff06b Mon Sep 17 00:00:00 2001 From: trixirt Date: Wed, 31 Jul 2024 12:42:40 -0700 Subject: [PATCH 04/14] Find ROCm on Fedora (#5705) ROCm is packaged natively on Fedora. It's install location do not match the AMD release. So add some Fedora specific logic to find the ROCm version and use rocminfo when attempts to use the AMD release fail. Signed-off-by: Tom Rix Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- op_builder/builder.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/op_builder/builder.py b/op_builder/builder.py index 458a43c3ce5c..f2ec52338c1e 100644 --- a/op_builder/builder.py +++ b/op_builder/builder.py @@ -4,6 +4,7 @@ # DeepSpeed Team import os +import re import sys import time import importlib @@ -215,6 +216,7 @@ def installed_rocm_version(): ROCM_MAJOR = '0' ROCM_MINOR = '0' + ROCM_VERSION_DEV_RAW = "" if OpBuilder.is_rocm_pytorch(): from torch.utils.cpp_extension import ROCM_HOME rocm_ver_file = Path(ROCM_HOME).joinpath(".info/version-dev") @@ -223,11 +225,22 @@ def installed_rocm_version(): ROCM_VERSION_DEV_RAW = file.read() elif "rocm" in torch.__version__: ROCM_VERSION_DEV_RAW = torch.__version__.split("rocm")[1] + if ROCM_VERSION_DEV_RAW != "": + ROCM_MAJOR = ROCM_VERSION_DEV_RAW.split('.')[0] + ROCM_MINOR = ROCM_VERSION_DEV_RAW.split('.')[1] else: + # Look in /usr/include/rocm-version.h + rocm_ver_file = Path("/usr/include/rocm_version.h") + if rocm_ver_file.is_file(): + with open(rocm_ver_file, 'r') as file: + for ln in file.readlines(): + if "#define ROCM_VERSION_MAJOR" in ln: + ROCM_MAJOR = re.findall(r'\S+', ln)[2] + elif "#define ROCM_VERSION_MINOR" in ln: + ROCM_MINOR = re.findall(r'\S+', ln)[2] + if ROCM_MAJOR == '0': assert False, "Could not detect ROCm version" - assert ROCM_VERSION_DEV_RAW != "", "Could not detect ROCm version" - ROCM_MAJOR = ROCM_VERSION_DEV_RAW.split('.')[0] - ROCM_MINOR = ROCM_VERSION_DEV_RAW.split('.')[1] + OpBuilder._rocm_version = (int(ROCM_MAJOR), int(ROCM_MINOR)) return OpBuilder._rocm_version @@ -235,7 +248,10 @@ def installed_rocm_version(): def get_rocm_gpu_arch(): if OpBuilder._rocm_gpu_arch: return OpBuilder._rocm_gpu_arch - rocm_gpu_arch_cmd = "/opt/rocm/bin/rocminfo | grep -o -m 1 'gfx.*'" + rocm_info = Path("/opt/rocm/bin/rocminfo") + if (not rocm_info.is_file()): + rocm_info = Path("rocminfo") + rocm_gpu_arch_cmd = str(rocm_info) + " | grep -o -m 1 'gfx.*'" try: result = subprocess.check_output(rocm_gpu_arch_cmd, shell=True) rocm_gpu_arch = result.decode('utf-8').strip() @@ -248,7 +264,12 @@ def get_rocm_gpu_arch(): def get_rocm_wavefront_size(): if OpBuilder._rocm_wavefront_size: return OpBuilder._rocm_wavefront_size - rocm_wavefront_size_cmd = "/opt/rocm/bin/rocminfo | grep -Eo -m1 'Wavefront Size:[[:space:]]+[0-9]+' | grep -Eo '[0-9]+'" + + rocm_info = Path("/opt/rocm/bin/rocminfo") + if (not rocm_info.is_file()): + rocm_info = Path("rocminfo") + rocm_wavefront_size_cmd = str( + rocm_info) + " | grep -Eo -m1 'Wavefront Size:[[:space:]]+[0-9]+' | grep -Eo '[0-9]+'" try: result = subprocess.check_output(rocm_wavefront_size_cmd, shell=True) rocm_wavefront_size = result.decode('utf-8').strip() From 681be6f55801a51278d83d8e9b52e9564bafcb77 Mon Sep 17 00:00:00 2001 From: Lev Kurilenko <113481193+lekurile@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:34:59 -0700 Subject: [PATCH 05/14] Fix CPU Adam JIT compilation (#5780) This PR fixes CPU Adam JIT compilation by including the `CUDA_LIB64` path in the `extra_ldflags` list before calling `load()`. --------- Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- op_builder/builder.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/op_builder/builder.py b/op_builder/builder.py index f2ec52338c1e..b1ca311d0355 100644 --- a/op_builder/builder.py +++ b/op_builder/builder.py @@ -800,25 +800,32 @@ def libraries_args(self): class TorchCPUOpBuilder(CUDAOpBuilder): + def get_cuda_lib64_path(self): + import torch + if not self.is_rocm_pytorch(): + CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.CUDA_HOME, "lib64") + if not os.path.exists(CUDA_LIB64): + CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.CUDA_HOME, "lib") + else: + CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.ROCM_HOME, "lib") + return CUDA_LIB64 + def extra_ldflags(self): if self.build_for_cpu: return ['-fopenmp'] if not self.is_rocm_pytorch(): - return ['-lcurand'] + ld_flags = ['-lcurand'] + if not self.build_for_cpu: + ld_flags.append(f'-L{self.get_cuda_lib64_path()}') + return ld_flags return [] def cxx_args(self): - import torch args = [] if not self.build_for_cpu: - if not self.is_rocm_pytorch(): - CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.CUDA_HOME, "lib64") - if not os.path.exists(CUDA_LIB64): - CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.CUDA_HOME, "lib") - else: - CUDA_LIB64 = os.path.join(torch.utils.cpp_extension.ROCM_HOME, "lib") + CUDA_LIB64 = self.get_cuda_lib64_path() args += super().cxx_args() args += [ From 324ee65cb0e5592cfa3a4d82273b2cd952b10a93 Mon Sep 17 00:00:00 2001 From: Joe Mayer <114769929+jomayeri@users.noreply.github.com> Date: Thu, 1 Aug 2024 06:15:10 -0700 Subject: [PATCH 06/14] GDS AIO Blog (#5817) README and media for the GDS blog. --------- Co-authored-by: Olatunji Ruwase Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- blogs/deepspeed-gds/README.md | 84 ++++++++++++++++++++++++++ blogs/deepspeed-gds/media/figure1.png | Bin 0 -> 32011 bytes blogs/deepspeed-gds/media/figure2.png | Bin 0 -> 40188 bytes blogs/deepspeed-gds/media/figure3.png | Bin 0 -> 44077 bytes blogs/deepspeed-gds/media/table1.png | Bin 0 -> 46740 bytes 5 files changed, 84 insertions(+) create mode 100644 blogs/deepspeed-gds/README.md create mode 100755 blogs/deepspeed-gds/media/figure1.png create mode 100755 blogs/deepspeed-gds/media/figure2.png create mode 100755 blogs/deepspeed-gds/media/figure3.png create mode 100755 blogs/deepspeed-gds/media/table1.png diff --git a/blogs/deepspeed-gds/README.md b/blogs/deepspeed-gds/README.md new file mode 100644 index 000000000000..a00b5083c3ea --- /dev/null +++ b/blogs/deepspeed-gds/README.md @@ -0,0 +1,84 @@ +
+ +# DeepNVMe: Improving DL Applications through I/O Optimizations + +
+ +# Introduction + +Deep Learning (DL) continues to drive unprecedented advancements across important +Artificial Intelligence domains including language, speech, video, and multimodal applications. +A key factor to these advancements is dramatic scalability on multiple dimensions including model size, +sequence length, and hardware parallelism. From a system perspective, DL scalability puts significant +pressure on essential subsystems including computation, memory, communication, and storage. However, +existing DL optimization efforts have mostly neglected the storage subsystem, making I/O operations such +as data loading, model checkpointing, and offloading the main bottlenecks of large-scale DL. To address +this problem, DeepSpeed has created a suite of I/O optimizations collectively called DeepNVMe. + +DeepNVMe improves the performance and efficiency of I/O-bound DL applications by accelerating I/O operations +and reducing hardware requirements. It achieves this by leveraging storage innovations such as Non-Volatile +Memory Express (NVMe) Solid Storage Devices (SSDs) and Nvidia Magnum IO^TM GPUDirect® Storage (GDS). In this +blog we show the benefits of DeepNVMe using microbenchmarks and an inference application. In experiments +conducted on an Azure NC96ads\_A100\_v4 VM, we observed that DeepNVMe saturates available NVMe bandwidth for +data transfers with GPU or CPU memory, achieving up to 10GB/sec reads and 5 GB/secs writes. + +# Background +High-performance access to persistent storage is a common challenge in many computing domains, including DL. Thus, a significant number of hardware and software solutions have been proposed. DeepNVMe builds on three such solutions: (1) NVMe SSDs, (2) Nvidia GDS, and (3) Linux Asynchronous I/O (libaio). We will briefly describe each of these technologies. + +NVMe SSDs are Flash-based storage devices that are replacing much slower hard disk drives (HDD) as primary persistent storage in modern servers. For example, an Azure NC96ads\_A100\_v4 VM is equipped with four NVMe SSDs which are individually capable of 3.25 GB/sec reads and can be combined in a RAID-0 configuration for a theoretical aggregate read bandwidth of 13 GB/sec. Nvidia GDS enables direct transfers between NVMe and GPU memory thus avoiding the inefficiencies of the traditional approach of using intermediate CPU memory (bounce buffer). Nvidia GDS is generally available in CUDA versions 11.4 and above. Finally, libaio is an asynchronous I/O stack introduced in Linux to better extract raw performance of fast storage devices like NVMe SSDs compared to the traditional I/O stack. + +# DeepNVMe: an Optimization Module for DeepLearning I/O + +DeepNVMe is a Python module that we developed with two key design principles. First, it leverages the above discussed storage technologies to implement powerful optimizations such as non-blocking I/O operations, bulk submission of I/O operations, parallelization of an individual I/O operation, and a lightweight runtime. Second, it exposes these I/O optimizations through a simple POSIX-like interface to foster easy integration into DL applications while avoiding the complexities of the underlying technologies. + +# Evaluation + +Our experiments are conducted on an Azure NC96ads\_A100\_v4 VM with setup details summarized in Table 1. For multi-device experiments, the SSDs are combined in a RAID-0 configuration. + + + +
+Table 1: Experimental setup details +
+ +## Microbenchmark Performance + +We used three benchmarking tools for our evaluations. The first is fio, the popular I/O benchmarking tool written in C. The second is gdsio from Nvidia for benchmarking GDS performance. The third is ds\_io, a Python tool that we created for easy integration with DeepNVMe and to be more representative of DL applications which are commonly Python-based. + +## High-Performance I/O with CPU Buffers via NVMe Scaling + +Our first set of microbenchmark evaluations used fio and ds\_io to measure the performance of transferring 1GB data between NVMe and CPU memory. We configure fio to use the libaio backend for these experiments1. The results are summarized in Figure 1, from which we make two observations. First, DeepNVMe demonstrates high performance as it roughly matches fio, despite being more representative of DL applications. Second, DeepNVMe scales I/O performance almost linearly with available NVMe bandwidth, achieving rates of 10GB/sec reads and 5GB/sec writes. + + + +
+Figure 1: Using DeepNVMe to scale data transfers between NVMe and CPU buffer +
+ +## High-Performance I/O with GPU Buffers via NVMe Scaling + +Our second set of microbenchmark evaluations used gdsio and ds\_io to measure the performance of 1GB data transfer between NVMe and GPU memory. For this experiment, we configure ds\_io to use both the traditional bounce buffer approach and the more efficient GDS approach. The results are summarized in Figure 2, from which we make three observations. First, we see that GDS improves performance in DeepNVMe compared to the traditional bounce buffer approach, with up to 37% speedup. Second, DeepNVMe demonstrates high performance by matching (and sometimes surpassing) gdsio despite being more representative of DL applications. Third, we see that DeepNVMe, with and without GDS, scales I/O performance with available NVMe bandwidth. With GDS, DeepNVMe achieves a maximum of 9.6GB/sec reads and 5GB/sec writes, and without GDS achieves 7GB/sec reads and 4GB/sec writes. + + + +
+Figure 2: Using DeepNVMe to scale data transfers between NVMe and GPU memory +
+ +## ZeRO-Inference: Generative AI Performance + +ZeRO-Inference is an AI democratization technology that reduces the hardware cost of inferencing massive models by using DeepNVMe to offload model weights to CPU or NVMe memory. ZeRO-Inference is well suited for throughput-oriented applications, such as offline inferencing, and for scenarios with limited hardware budget. We use token generation workload to evaluate DeepNVMe performance for NVMe offloading. + +## High-Performance Offloading via NVMe Scaling + +We measure the generation throughput of inferencing a LLAMA3-70B model on a single NVIDIA A100-80GB with a prompt length of 512, generation length of 32, and batch size of 96. We scale the number of NVMe SSDs from 1 to 4 and present the results for ZeRO-Inference with and without GDS in Figure 3. We make two observations from these results. First, GDS consistently provides better performance compared to the bounce buffer approach, achieving 10-18% faster token generation. Second, DeepNVMe, with and without GDS, scales generation performance with available NVMe bandwidth. With four NVMe SSDs, DeepNVMe achieves generation throughput rates of 7 tokens per second with GDS and 6 tokens per second without GDS. Our profiling results suggest that DeepNVMe will continue to scale with more NVMe bandwidth, making it an economic option for boosting generative application performance. + + + +
+Figure 3: Using DeepNVMe to scale LLAMA3-70B token generation performance with NVMe offloading. +
+ +# Conclusion + +In this blog post, we introduced DeepNVMe, an I/O optimization technology created to tackle the emergence of I/O operations as key bottlenecks of Deep Learning scalability. DeepNVMe enables fast and efficient data transfers between persistent storage and DL application memory through optimizations built on popular storage technologies such as NVMe SSDs and NVIDIA GDS. We showed benefits of using DeepNVMe for LLAMA3-70B token generation on single A100-80GB GPU with NVMe offloading, for which it achieves up to 7 tokens per second in generation throughput on an Azure NC96ads\_A100\_v4 VM. DeepNVMe will be generally available in DeepSpeed versions >= [0.15.0](https://github.com/microsoft/DeepSpeed/releases/tag/v0.15.0). In future blogs, we will report DeepNVMe improvements for other I/O bound DL applications such as model checkpointing and data loading. diff --git a/blogs/deepspeed-gds/media/figure1.png b/blogs/deepspeed-gds/media/figure1.png new file mode 100755 index 0000000000000000000000000000000000000000..08db7d2f8afaf4008b6b74f22f8372ac8998f9f6 GIT binary patch literal 32011 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 Q2blkXQ&5-Bku&lB545vo=l}o! literal 0 HcmV?d00001 diff --git a/blogs/deepspeed-gds/media/figure2.png b/blogs/deepspeed-gds/media/figure2.png new file mode 100755 index 0000000000000000000000000000000000000000..35be5d4c401588b89d6d2b4a3a7bf480c525d6c9 GIT binary patch literal 40188 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

mV|2G@vl+;r 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

11|`vy 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

u^x%?-ru_~M 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 literal 0 HcmV?d00001 From f82d08862f94b54762b444a2b76c09574ced520a Mon Sep 17 00:00:00 2001 From: Ramya Ramineni <62723901+rraminen@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:13:40 -0500 Subject: [PATCH 07/14] [ROCm] Get rocm version from /opt/rocm/.info/version (#5815) Previously we used to get ROCm version information from /opt/rocm/.info/version-dev file. This PR is to modify the code to get ROCm version from /opt/rocm/.info/version file instead to add compatibility with ROCm Centos9 docker images. cc: @jithunnair-amd Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- op_builder/builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_builder/builder.py b/op_builder/builder.py index b1ca311d0355..8998fc0eddb8 100644 --- a/op_builder/builder.py +++ b/op_builder/builder.py @@ -219,7 +219,7 @@ def installed_rocm_version(): ROCM_VERSION_DEV_RAW = "" if OpBuilder.is_rocm_pytorch(): from torch.utils.cpp_extension import ROCM_HOME - rocm_ver_file = Path(ROCM_HOME).joinpath(".info/version-dev") + rocm_ver_file = Path(ROCM_HOME).joinpath(".info/version") if rocm_ver_file.is_file(): with open(rocm_ver_file, 'r') as file: ROCM_VERSION_DEV_RAW = file.read() From 17ed7c77c58611a923a6c8d2a3d21d359cd046e8 Mon Sep 17 00:00:00 2001 From: inkcherry Date: Fri, 2 Aug 2024 00:14:36 +0800 Subject: [PATCH 08/14] sequence parallel with communication overlap (#5691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SP is a fantastic piece of work, it is very elegant and concise, at the current stage, a transformer layer's forward and backward passes involve 8 all-to-all operations, with 5 opportunities for overlapping communication: Forward pass: The QKV matrix operations can be pipelined alongside some of the all-to-all communications. Backward pass: DQ, DK, DV all-to-all communications can be pipelined alongside matrix operations. Backward pass: DO_w can be parallel with DO_input, involving matrix operations and all-to-all communications. Similar overlap-comm strategies are used in Megatron for TP/TP-sp parallelism. I tested under conditions of 1N8C zero1, disabled activation checkpointing, ds-sp=8, and gbs=16: 1B 64K 7B 16K They showed over 10% improvement (where I found that for mega-ds, using split QKV itself can also enhance performance due to reducing slice + cat operations in fwd/bwd), despite some TFLOPs already performing at a relatively good level. co-work with https://github.com/microsoft/Megatron-DeepSpeed/pull/415 --------- Co-authored-by: Olatunji Ruwase Co-authored-by: Heyang Qin --- deepspeed/sequence/layer.py | 141 +++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 19 deletions(-) diff --git a/deepspeed/sequence/layer.py b/deepspeed/sequence/layer.py index a715843d8eff..f17cfa883cc6 100644 --- a/deepspeed/sequence/layer.py +++ b/deepspeed/sequence/layer.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 # DeepSpeed Team - import torch from typing import Any, Tuple @@ -10,9 +9,21 @@ from torch.nn import Module import deepspeed.comm as dist +from deepspeed.accelerator import get_accelerator + + +def post_all2all(transpose, res_shape): + + def post_func(input): + if transpose: + input = input.transpose(0, 2).contiguous() + input = input.reshape(res_shape) + return input + return post_func -def single_all_to_all(input, scatter_idx, gather_idx, group): + +def single_all_to_all(input, scatter_idx, gather_idx, group, async_op=False, handle=None, type=None): seq_world_size = dist.get_world_size(group) inp_shape = list(input.shape) inp_shape[scatter_idx] = inp_shape[scatter_idx] // seq_world_size @@ -29,32 +40,76 @@ def single_all_to_all(input, scatter_idx, gather_idx, group): ).transpose(0, 1).contiguous() output = torch.empty_like(input_t) - dist.all_to_all_single(output, input_t, group=group) - - # if scattering the seq-dim, transpose the heads back to the original dimension - if scatter_idx < 2: - output = output.transpose(0, 2).contiguous() + work = dist.all_to_all_single(output, input_t, group=group, async_op=async_op) - return output.reshape( - inp_shape[: gather_idx] + \ + res_shape=( inp_shape[: gather_idx] + \ [inp_shape[gather_idx] * seq_world_size,] + \ - inp_shape[gather_idx + 1:]).contiguous() + inp_shape[gather_idx + 1:]) + transpose = True if scatter_idx < 2 else False + post_all2all_fun = post_all2all(transpose, res_shape) + + if async_op: + if type in ('dq', 'dk'): + handle[type + '_work'] = work + handle[type + '_grad'] = output + handle[type + '_post_all2all_func'] = post_all2all_fun + return output.view(res_shape) + + res = post_all2all_fun(output) + return res class _SeqAllToAll(torch.autograd.Function): @staticmethod - def forward(ctx: Any, group: dist.ProcessGroup, input: Tensor, scatter_idx: int, gather_idx: int) -> Tensor: - + def forward(ctx: Any, + group: dist.ProcessGroup, + input: Tensor, + scatter_idx: int, + gather_idx: int, + stream=None, + handle=None, + type=None, + is_fwd=True) -> Tensor: ctx.group = group ctx.scatter_idx = scatter_idx ctx.gather_idx = gather_idx - - return single_all_to_all(input, scatter_idx, gather_idx, group) + ctx.stream = stream + ctx.handle = handle + ctx.type = type + if ctx.handle is None: + res = single_all_to_all(input, scatter_idx, gather_idx, group, False) + + else: + # overlap communication path + if not is_fwd and type == 'o': + assert ctx.stream != None + res = single_all_to_all(input, scatter_idx, gather_idx, group, False) + get_accelerator().current_stream().wait_stream(ctx.stream) + del ctx.stream.activation_buffer_list + # The computation of d o_weight can overlap with the communication of d o_input + + elif not is_fwd and type in ('q', 'k'): + # Achieve communication overlap by pipelining the matrix computation and communication of dq, dk, and dv + type = 'd' + type + res = single_all_to_all(input, scatter_idx, gather_idx, group, True, handle, type) + + elif is_fwd and type in ('q', 'k'): + # Achieve communication overlap by pipelining the matrix computation and communication of q, k, and v + type = 'fwd_' + type + res = single_all_to_all(input, scatter_idx, gather_idx, group, False, handle, type) + + else: + res = single_all_to_all(input, scatter_idx, gather_idx, group, False) + + return res @staticmethod def backward(ctx: Any, *grad_output: Tensor) -> Tuple[None, Tensor, None, None]: - return (None, _SeqAllToAll.apply(ctx.group, *grad_output, ctx.gather_idx, ctx.scatter_idx), None, None) + + return (None, + _SeqAllToAll.apply(ctx.group, *grad_output, ctx.gather_idx, ctx.scatter_idx, ctx.stream, ctx.handle, + ctx.type, False), None, None, None, None, None, None) class DistributedAttention(torch.nn.Module): @@ -73,6 +128,7 @@ def __init__( sequence_process_group: dist.ProcessGroup, scatter_idx: int = 2, gather_idx: int = 0, + sp_stream=None, ) -> None: super(DistributedAttention, self).__init__() @@ -80,6 +136,17 @@ def __init__( self.spg = sequence_process_group self.scatter_idx = scatter_idx self.gather_idx = gather_idx + self.sp_overlap_comm = False + self.overlap_handles = None + self.sp_stream = sp_stream + if sp_stream is not None: + self.overlap_handles = {} + self.sp_overlap_comm = True + self.dafult_stream = get_accelerator().default_stream() + + def layer_sync(self, layer): + if self.sp_overlap_comm and hasattr(layer, 'done_event'): + self.dafult_stream.wait_event(layer.done_event) def forward(self, query: Tensor, key: Tensor, value: Tensor, *args: Any, **kwargs) -> Tensor: """ forward @@ -93,17 +160,53 @@ def forward(self, query: Tensor, key: Tensor, value: Tensor, *args: Any, **kwarg Returns: * output (Tensor): context output """ + # TODO Merge three alltoall calls into one # TODO (Reza): change the api on the megatron-deepspeed side so that we only receive all data (q,k, and v) together! #in shape : e.g., [s/p:h:] - query_layer = _SeqAllToAll.apply(self.spg, query, self.scatter_idx, self.gather_idx) - key_layer = _SeqAllToAll.apply(self.spg, key, self.scatter_idx, self.gather_idx) - value_layer = _SeqAllToAll.apply(self.spg, value, self.scatter_idx, self.gather_idx) + + def bwd_hook(layer_type): + + def pre_hook_fun(grad): + type = 'd' + layer_type + self.overlap_handles[type + '_work'].wait() + self.sp_stream.wait_stream(self.dafult_stream) + all2all_output = self.overlap_handles[type + '_grad'] + grad = list(grad) + grad[0] = self.overlap_handles[type + '_post_all2all_func'](all2all_output) + grad = tuple(grad) + + return pre_hook_fun + + self.layer_sync(query) + query_layer = _SeqAllToAll.apply(self.spg, query, self.scatter_idx, self.gather_idx, None, + self.overlap_handles, 'q') + self.layer_sync(key) + key_layer = _SeqAllToAll.apply(self.spg, key, self.scatter_idx, self.gather_idx, None, self.overlap_handles, + 'k') + if self.sp_overlap_comm: + self.dafult_stream.wait_stream(self.sp_stream) + + value_layer = _SeqAllToAll.apply(self.spg, value, self.scatter_idx, self.gather_idx, None, + self.overlap_handles, 'v') + + if self.sp_overlap_comm: + # Register a hook to synchronize dq and dk after the all-to-all + # operation when the gradient data is used. + # Place this logic after the q, k, v all-to-all operation to + # improve interpreter speed to + # call and launch of the forward all-to-all communication. + grad_fn_q = query.grad_fn.next_functions[0][0] + grad_fn_q.register_prehook(bwd_hook(layer_type='q')) + grad_fn_k = key.grad_fn.next_functions[0][0] + grad_fn_k.register_prehook(bwd_hook(layer_type='k')) #out shape : e.g., [s:h/p:] + context_layer = self.local_attn(query_layer, key_layer, value_layer, *args, **kwargs) - output = _SeqAllToAll.apply(self.spg, context_layer, self.gather_idx, self.scatter_idx) + output = _SeqAllToAll.apply(self.spg, context_layer, self.gather_idx, self.scatter_idx, self.sp_stream, + self.overlap_handles, 'o') #out e.g., [s/p::h] return output From 23d0e0221f12f7a6d2aa93c7964fe192ec38e0c4 Mon Sep 17 00:00:00 2001 From: Logan Adams <114770087+loadams@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:25:08 -0700 Subject: [PATCH 09/14] Update to ROCm6 (#5491) --- .github/workflows/amd-mi200.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/amd-mi200.yml b/.github/workflows/amd-mi200.yml index cd1cafe8e679..ea8d2f5f806f 100644 --- a/.github/workflows/amd-mi200.yml +++ b/.github/workflows/amd-mi200.yml @@ -32,7 +32,7 @@ jobs: - name: Install pytorch run: | - pip install -U --cache-dir $TORCH_CACHE torch torchvision --index-url https://download.pytorch.org/whl/rocm5.6 + pip install -U --cache-dir $TORCH_CACHE torch torchvision --index-url https://download.pytorch.org/whl/rocm6.0 python -c "import torch; print('torch:', torch.__version__, torch)" python -c "import torch; print('CUDA available:', torch.cuda.is_available())" From 249c1db2fb7a98e0c7baeb1bee0548e9bc5f986b Mon Sep 17 00:00:00 2001 From: Perry Zou Date: Fri, 2 Aug 2024 01:27:24 +0800 Subject: [PATCH 10/14] Add fp16 support of Qwen1.5MoE models (A2.7B) to DeepSpeed-FastGen (#5403) This PR adds support for Qwen1.5MoE-A2.7B models. support for https://github.com/microsoft/DeepSpeed-MII/issues/457 ### Test Code for mii pipeline: ```python import mii pipe = mii.pipeline("/data/zonepg/models/Qwen/Qwen1.5-MoE-A2.7B") responses = pipe("DeepSpeed is", max_new_tokens=128, do_sample=False) if pipe.is_rank_0: print(responses[0]) ``` for huggingface: ```python import mii from transformers import AutoModelForCausalLM, AutoTokenizer from transformers.generation import GenerationConfig import torch tokenizer = AutoTokenizer.from_pretrained("/data/zonepg/models/Qwen/Qwen1.5-MoE-A2.7B") model = AutoModelForCausalLM.from_pretrained("/data/zonepg/models/Qwen/Qwen1.5-MoE-A2.7B", device_map="auto", torch_dtype=torch.float16, trust_remote_code=True).eval() print(model) inputs = tokenizer('DeepSpeed is', return_tensors='pt') inputs = inputs.to(model.device) pred = model.generate(**inputs, max_new_tokens=128, do_sample=False, repetition_penalty=1.0) test = tokenizer.decode(pred.cpu()[0], skip_special_tokens=False) print(test) ``` ### Qwen1.5-MoE-A2.7B Huggingface output with prompt "DeepSpeed is": ``` a deep learning framework that is designed to accelerate the training of large-scale neural networks. It is built on top of PyTorch and provides a set of tools and techniques for optimizing the performance of deep learning models. DeepSpeed supports a variety of hardware accelerators, including GPUs, TPUs, and FPGAs, and can be used to train models on distributed systems, such as clusters of GPUs or TPUs. One of the key features of DeepSpeed is its ability to automatically parallelize the training of deep learning models across multiple GPUs or TPUs. This can significantly reduce the time required to train large models, as it allows the ``` DeepSpeed-FastGen output with prompt "DeepSpeed is": ``` a deep learning framework that is designed to accelerate the training of large-scale neural networks. It is built on top of PyTorch and provides a set of tools and techniques for optimizing the performance of deep learning models. DeepSpeed supports a variety of hardware accelerators, including GPUs, TPUs, and FPGAs, and can be used to train models on distributed systems, such as clusters of GPUs or TPUs. One of the key features of DeepSpeed is its ability to automatically parallelize the training of deep learning models across multiple GPUs or TPUs. This can significantly reduce the time required to train large models, as it allows the ``` DeepSpeed-FastGen output with prompt "DeepSpeed is" with 8-way sharding: ``` a deep learning framework that is designed to accelerate the training of large-scale neural networks. It is built on top of PyTorch and provides a set of tools and techniques for optimizing the performance of deep learning models. DeepSpeed supports a variety of hardware accelerators, including GPUs, TPUs, and FPGAs, and can be used to train models on distributed systems, such as clusters of GPUs or TPUs. One of the key features of DeepSpeed is its ability to automatically parallelize the training of deep learning models across multiple GPUs or TPUs. This can significantly reduce the time required to train large models, as it allows the ``` Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> Co-authored-by: Heyang Qin Co-authored-by: Abhishek Kulkarni <11399+adk9@users.noreply.github.com> --- blogs/deepspeed-fastgen/README.md | 2 + deepspeed/inference/v2/engine_factory.py | 3 + .../kernels/ragged_ops/includes/top_k_utils.h | 3 + .../v2/model_implementations/__init__.py | 1 + .../qwen_v2_moe/__init__.py | 6 + .../qwen_v2_moe/container.py | 103 +++++ .../qwen_v2_moe/model.py | 359 ++++++++++++++++++ .../qwen_v2_moe/policy.py | 30 ++ .../implementations/moe/cutlass_multi_gemm.py | 2 +- 9 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 deepspeed/inference/v2/model_implementations/qwen_v2_moe/__init__.py create mode 100644 deepspeed/inference/v2/model_implementations/qwen_v2_moe/container.py create mode 100644 deepspeed/inference/v2/model_implementations/qwen_v2_moe/model.py create mode 100644 deepspeed/inference/v2/model_implementations/qwen_v2_moe/policy.py diff --git a/blogs/deepspeed-fastgen/README.md b/blogs/deepspeed-fastgen/README.md index 2585fac5d26e..e287af2540ed 100644 --- a/blogs/deepspeed-fastgen/README.md +++ b/blogs/deepspeed-fastgen/README.md @@ -233,6 +233,8 @@ We currently support the following model architectures in this alpha release of * [Phi-2](https://huggingface.co/models?other=phi-msft) * [Phi-3](https://huggingface.co/models?other=phi3) * [Qwen](https://huggingface.co/models?other=qwen) +* [Qwen2](https://huggingface.co/models?other=qwen2) +* [Qwen2-MoE](https://huggingface.co/models?other=qwen2_moe) All current models leverage [HuggingFace](https://github.com/huggingface) APIs in our backend to provide both the model weights and the model's corresponding tokenizer. diff --git a/deepspeed/inference/v2/engine_factory.py b/deepspeed/inference/v2/engine_factory.py index c21affb9a0de..314f7f2f0485 100644 --- a/deepspeed/inference/v2/engine_factory.py +++ b/deepspeed/inference/v2/engine_factory.py @@ -23,6 +23,7 @@ Phi3Policy, QwenPolicy, Qwen2Policy, + Qwen2MoePolicy, ) from .model_implementations.inference_policy_base import POLICIES, InferenceV2Policy from .model_implementations.flat_model_helpers import make_metadata_filename, ModelMetadata @@ -126,6 +127,8 @@ def build_hf_engine(path: str, policy = QwenPolicy(model_config, checkpoint_engine=checkpoint_engine) elif model_config.model_type == "qwen2": policy = Qwen2Policy(model_config, checkpoint_engine=checkpoint_engine) + elif model_config.model_type == "qwen2_moe": + policy = Qwen2MoePolicy(model_config, checkpoint_engine=checkpoint_engine) else: raise ValueError(f"Unsupported model type {model_config.model_type}") diff --git a/deepspeed/inference/v2/kernels/ragged_ops/includes/top_k_utils.h b/deepspeed/inference/v2/kernels/ragged_ops/includes/top_k_utils.h index abb9e15f8f6f..2cc430ccfe34 100644 --- a/deepspeed/inference/v2/kernels/ragged_ops/includes/top_k_utils.h +++ b/deepspeed/inference/v2/kernels/ragged_ops/includes/top_k_utils.h @@ -11,5 +11,8 @@ } else if (2 == N_TOP_K) { \ constexpr int CONST_TOP_K = 2; \ __VA_ARGS__(); \ + } else if (4 == N_TOP_K) { \ + constexpr int CONST_TOP_K = 4; \ + __VA_ARGS__(); \ } \ }() diff --git a/deepspeed/inference/v2/model_implementations/__init__.py b/deepspeed/inference/v2/model_implementations/__init__.py index e4160ab94949..3483d9348c55 100644 --- a/deepspeed/inference/v2/model_implementations/__init__.py +++ b/deepspeed/inference/v2/model_implementations/__init__.py @@ -18,3 +18,4 @@ from .phi3 import * from .qwen import * from .qwen_v2 import * +from .qwen_v2_moe import * diff --git a/deepspeed/inference/v2/model_implementations/qwen_v2_moe/__init__.py b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/__init__.py new file mode 100644 index 000000000000..23e06a770023 --- /dev/null +++ b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +from .policy import Qwen2MoePolicy diff --git a/deepspeed/inference/v2/model_implementations/qwen_v2_moe/container.py b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/container.py new file mode 100644 index 000000000000..b4621257ff82 --- /dev/null +++ b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/container.py @@ -0,0 +1,103 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +# Create a container object to save model-specific tensors using the policy file above. + +from ..common_parameters import * +from ..layer_container_base import LayerContainer +''' + # HF Qwen1.5-MoE-A2.7B model looks like this: + +Qwen2MoeForCausalLM( + (model): Qwen2MoeModel( + (embed_tokens): Embedding(151936, 2048) + (layers): ModuleList( + (0-23): 24 x Qwen2MoeDecoderLayer( + (self_attn): Qwen2MoeSdpaAttention( + (q_proj): Linear(in_features=2048, out_features=2048, bias=True) + (k_proj): Linear(in_features=2048, out_features=2048, bias=True) + (v_proj): Linear(in_features=2048, out_features=2048, bias=True) + (o_proj): Linear(in_features=2048, out_features=2048, bias=False) + (rotary_emb): Qwen2MoeRotaryEmbedding() + ) + (mlp): Qwen2MoeSparseMoeBlock( + (gate): Linear(in_features=2048, out_features=60, bias=False) + (experts): ModuleList( + (0-59): 60 x Qwen2MoeMLP( + (gate_proj): Linear(in_features=2048, out_features=1408, bias=False) + (up_proj): Linear(in_features=2048, out_features=1408, bias=False) + (down_proj): Linear(in_features=1408, out_features=2048, bias=False) + (act_fn): SiLU() + ) + ) + (shared_expert): Qwen2MoeMLP( + (gate_proj): Linear(in_features=2048, out_features=5632, bias=False) + (up_proj): Linear(in_features=2048, out_features=5632, bias=False) + (down_proj): Linear(in_features=5632, out_features=2048, bias=False) + (act_fn): SiLU() + ) + (shared_expert_gate): Linear(in_features=2048, out_features=1, bias=False) + ) + (input_layernorm): Qwen2MoeRMSNorm() + (post_attention_layernorm): Qwen2MoeRMSNorm() + ) + ) + (norm): Qwen2MoeRMSNorm() + ) + (lm_head): Linear(in_features=2048, out_features=151936, bias=False) +) +''' + + +class Qwen2MoeTransformerContainer(LayerContainer): + """ + Transformer layer container for the Qwen2Moe model. + """ + qkv_w: UnfusedQKVParameter + qkv_b: UnfusedQKVParameter + attn_out_w: AttentionOutputParameter + moe_gate: MoEGatingWeightParameter + moe_mlp_1: UnfusedMoEGatedMLPParameter + moe_mlp_2: UnfusedMoEMLP2Parameter + shared_moe_mlp_1: GatedMLPParameter + shared_moe_mlp_2: MLP2Parameter + shared_moe_gate: MoEGatingWeightParameter + attn_norm_gamma: NormParameter + mlp_norm_gamma: NormParameter + + PARAM_MAPPING = { + "self_attn.q_proj.weight": "qkv_w.q_params", + "self_attn.k_proj.weight": "qkv_w.k_params", + "self_attn.v_proj.weight": "qkv_w.v_params", + "self_attn.q_proj.bias": "qkv_b.q_params", + "self_attn.k_proj.bias": "qkv_b.k_params", + "self_attn.v_proj.bias": "qkv_b.v_params", + "self_attn.o_proj.weight": "attn_out_w.params", + "mlp.gate.weight": "moe_gate.params", + "mlp.experts.*.gate_proj.weight": "moe_mlp_1.gating_experts", + "mlp.experts.*.up_proj.weight": "moe_mlp_1.up_experts", + "mlp.experts.*.down_proj.weight": "moe_mlp_2.experts", + "mlp.shared_expert.gate_proj.weight": "shared_moe_mlp_1.gate_params", + "mlp.shared_expert.up_proj.weight": "shared_moe_mlp_1.up_params", + "mlp.shared_expert.down_proj.weight": "shared_moe_mlp_2.params", + "mlp.shared_expert_gate.weight": "shared_moe_gate.params", + "input_layernorm.weight": "attn_norm_gamma.params", + "post_attention_layernorm.weight": "mlp_norm_gamma.params", + } + + +class Qwen2MoeNonTransformerContainer(LayerContainer): + """ + Non-Transformer layer container for the Qwen2Moe model. + """ + word_emb: EmbeddingParameter + word_unembed: UnembedParameter + final_norm: NormParameter + + PARAM_MAPPING = { + "model.embed_tokens.weight": "word_emb.params", + "model.norm.weight": "final_norm.params", + "lm_head.weight": "word_unembed.params", + } diff --git a/deepspeed/inference/v2/model_implementations/qwen_v2_moe/model.py b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/model.py new file mode 100644 index 000000000000..7cddbf978369 --- /dev/null +++ b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/model.py @@ -0,0 +1,359 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +from typing import Iterable, Optional, Tuple + +import torch + +import deepspeed.comm as dist + +from ...allocator import empty_from +from ...config_v2 import RaggedInferenceEngineConfig +from ...inference_utils import ActivationType, DtypeEnum +from ...model_implementations import * +from ...modules.configs import * +from ...modules.interfaces import * +from ...modules import heuristics +from ...ragged import RaggedBatchWrapper +from ..inference_model_base import ( + DSModelImplementationConfig, + MPType, +) + +from .container import Qwen2MoeNonTransformerContainer, Qwen2MoeTransformerContainer + + +class Qwen2MoeInferenceModel(DSMoETransformerModelBase): + """ + Inference model implementation for Qwen2MoE models. + """ + + _non_transformer: Optional[Qwen2MoeNonTransformerContainer] + """ + Embed + unembed container. Specializing the type annotation. + """ + + _transformer: Optional[Iterable[Qwen2MoeTransformerContainer]] + """ + Per-layer transformer container. Specializing the type annotation. + """ + """ + Properties ineherited from `DSInferenceModelBase` + """ + + @property + def max_sequence_length(self) -> int: + return self._config.max_position_embeddings + + """ + Properties ineherited from `DSTransformerModelBase` + """ + + @property + def num_layers(self) -> int: + return self._config.num_hidden_layers + + @property + def model_dim(self) -> int: + return self._config.hidden_size + + @property + def vocab_size(self) -> int: + return self._config.vocab_size + + @property + def head_size(self) -> int: + return self.model_dim // self.n_heads + + @property + def n_heads(self) -> int: + return self._config.num_attention_heads + + @property + def intermediate_dim(self) -> int: + return self._config.intermediate_size + + @property + def n_heads_kv(self) -> int: + return self._config.num_key_value_heads + + @property + def activation_dtype(self) -> DtypeEnum: + # TODO(ZonePG): bf16 inference results may be different from huggingface bf16, + # because in rms_norm, Qwen still use float() instead of bf16 + # if self._config.torch_dtype == torch.float16: + # return DtypeEnum.fp16 + # elif self._config.torch_dtype == torch.bfloat16: + # return DtypeEnum.bf16 + # else: + # raise NotImplementedError("Only fp16 and bf16 are supported") + return DtypeEnum.fp16 + + @property + def mlp_activation_fn(self) -> ActivationType: + return ActivationType.SiGLU + + @property + def norm_type(self) -> NormTypeEnum: + return NormTypeEnum.RMSNorm + + @property + def positional_embedding_type(self) -> PositionalEmbeddingType: + return PositionalEmbeddingType.rotate_half + + @property + def positional_embedding_config(self) -> Optional[RotateHalfConfig]: + return RotateHalfConfig(theta_base=self._config.rope_theta) + + """ + Inherited from `DSMoETransformerModelBase` + """ + + @property + def n_experts(self) -> int: + return self._config.num_experts + + @property + def n_top_k(self) -> int: + return self._config.num_experts_per_tok + + @property + def normalize_expert_scores(self) -> bool: + return self._config.norm_topk_prob + + def make_moe_layer(self) -> None: + """ + Instantiates the MoE layer for the model. This sets the `self.moe` attribute. + """ + sharded_dim = sharded_intermediate_dim(self.intermediate_dim // self.n_top_k, self.tp_size, self.tp_rank) + + moe_config = DSMoEConfig( + max_tokens=self._engine_config.state_manager.max_ragged_batch_size, + model_dim=self.model_dim, + intermediate_features=sharded_dim, + activation=self.mlp_activation_fn, + n_experts=self.n_experts, + top_k=self.n_top_k, + input_dtype=self.activation_dtype, + output_dtype=self.activation_dtype, + normalize_scores=self.normalize_expert_scores, + ) + + self.moe = heuristics.instantiate_moe(moe_config, self._engine_config) + + ######### MLP 1 ######### + def make_shared_expert_mlp_1_layer(self) -> None: + """ + Instantiates the linear projection layer for the first MLP in the feedforward network. + This sets the `self.mlp_1` attribute. + """ + shard_size = sharded_intermediate_dim(self.intermediate_dim, self.tp_size, self.tp_rank) + + linear_config = DSLinearConfig( + max_tokens=self._engine_config.state_manager.max_ragged_batch_size, + in_channels=self.model_dim, + out_channels=shard_size, + activation=self.mlp_activation_fn, + input_dtype=self.activation_dtype, + output_dtype=self.activation_dtype, + ) + + self.shared_expert_mlp_1 = heuristics.instantiate_linear(linear_config, self._engine_config) + + ######### MLP 2 ######### + def make_shared_expert_mlp_2_layer(self) -> None: + """ + Instantiates the linear projection layer for the second MLP in the feedforward network. + This sets the `self.mlp_2` attribute. + """ + shard_size = sharded_intermediate_dim(self.intermediate_dim, self.tp_size, self.tp_rank) + + linear_config = DSLinearConfig( + max_tokens=self._engine_config.state_manager.max_ragged_batch_size, + in_channels=shard_size, + out_channels=self.model_dim, + input_dtype=self.activation_dtype, + output_dtype=self.activation_dtype, + ) + + self.shared_expert_mlp_2 = heuristics.instantiate_linear(linear_config, self._engine_config) + + ######### MLP 2 ######### + def make_shared_expert_gate_layer(self) -> None: + """ + Instantiates the linear projection layer for the second MLP in the feedforward network. + This sets the `self.mlp_2` attribute. + """ + shard_size = sharded_intermediate_dim(self.model_dim, self.tp_size, self.tp_rank) + + linear_config = DSLinearConfig( + max_tokens=self._engine_config.state_manager.max_ragged_batch_size, + in_channels=shard_size, + out_channels=8, + input_dtype=self.activation_dtype, + output_dtype=self.activation_dtype, + ) + + self.shared_expert_gate = heuristics.instantiate_linear(linear_config, self._engine_config) + + def make_norm_layer(self) -> None: + """ + Instantiates the normalization layer for the model. This sets the `self.norm` attribute. + + TODO(cmikeh2): In the future we'll distinguish between the different norm objects, + but for now we'll just use the same one for all of them. + """ + norm_config = DSNormConfig( + max_tokens=self._engine_config.state_manager.max_ragged_batch_size, + type=self.norm_type, + channels=self.model_dim, + residual_dtype=self.activation_dtype, + input_dtype=self.activation_dtype, + output_dtype=self.activation_dtype, + eps=self._config.rms_norm_eps, + ) + + self.norm = heuristics.instantiate_pre_norm(norm_config, self._engine_config) + + """ + Model implementation + """ + + def __init__(self, config: DSModelImplementationConfig, engine_config: RaggedInferenceEngineConfig, + base_mp_group: MPType) -> None: + """ + Base implementation for initialization. By default, this will initialize + the traditional components of a transformer model: + - Embedding + - QKV projection + - Self attention + - Attention output projection + - Feed forward network + - Normalization + - Unembedding + + Arguments: + config (DSModelImplementationConfig): Model-specific configuration. No assumptions + should be made about this config that are not closely tied to the specific + model implementation. + engine_config (RaggedInferenceEngineConfig): Engine configuration. + base_mp_group (MPType): Base communication group for Tensor-parallel inference. + """ + super().__init__(config, engine_config, base_mp_group) + + self.make_norm_layer() + self.make_qkv_layer() + self.make_attn_layer() + self.make_attn_out_layer() + self.make_moe_layer() + self.make_shared_expert_mlp_1_layer() + self.make_shared_expert_mlp_2_layer() + self.make_shared_expert_gate_layer() + self.make_embedding_layer() + self.make_unembedding_layer() + self._kv_cache_config = None + + """ + Forward implementations + """ + + def _forward_embed(self, ragged_batch: RaggedBatchWrapper) -> torch.Tensor: + """ + Performs the embedding lookup prior to running the transformer of the model. + + Arguments: + ragged_batch (RaggedBatchWrapper): The batch to embed. + + Returns: + torch.Tensor: The embedded batch. + """ + embed = self.embed(ragged_batch, self._non_transformer.word_emb) + + if embed.shape[-1] != self.model_dim: + raise ValueError(f"Embedding output shape {embed.shape} does not match model_dim {self.model_dim}") + + return embed + + def _forward_transformer(self, layer_idx: int, residual: torch.Tensor, hidden_states: torch.Tensor, + ragged_batch_info: RaggedBatchWrapper) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Executes one (slightly offset) layer of the transformer. This implementation does a peak-ahead + optimization to fuse the layer norm of the next layer into the current layer. + + Arguments: + layer_idx (int): The index of the layer to execute. + residual (torch.Tensor): The residual tensor from the previous layer. + hidden_states (torch.Tensor): The hidden states from the previous layer. This is the + hidden states after pre normalization. + ragged_batch_info (RaggedBatchWrapper): The batch metadata. + """ + # TODO(cmikeh2): Distribute ragged_batch_info to all modules + + cur_params = self._transformer[layer_idx] + kv_cache = self.state_manager.get_cache(layer_idx) + + hidden_states = self.qkv(hidden_states, cur_params.qkv_w, b=cur_params.qkv_b) + hidden_states = self.attn(hidden_states, kv_cache, ragged_batch_info) + hidden_states = self.attn_out(hidden_states, cur_params.attn_out_w, b=None) + + if self.tp_size > 1: + dist.all_reduce(hidden_states, group=self._base_mp_group) + + residual, hidden_states = self.norm(residual, hidden_states, cur_params.mlp_norm_gamma, beta=None) + + shared_expert_output = self.shared_expert_mlp_1(hidden_states, cur_params.shared_moe_mlp_1, b=None) + shared_expert_output = self.shared_expert_mlp_2(shared_expert_output, cur_params.shared_moe_mlp_2, b=None) + shared_expert_gate_output = self.shared_expert_gate(hidden_states, cur_params.shared_moe_gate, b=None)[..., :1] + # shared_expert_gate_output shape[-1] is 1 + shared_expert_output.mul_(torch.sigmoid(shared_expert_gate_output)) + hidden_states = self.moe(hidden_states, ragged_batch_info, cur_params.moe_gate, cur_params.moe_mlp_1, + cur_params.moe_mlp_2) + hidden_states.add_(shared_expert_output) + + if self.tp_size > 1: + dist.all_reduce(hidden_states, group=self._base_mp_group) + + if layer_idx != self.num_layers - 1: + next_params = self._transformer[layer_idx + 1] + residual, hidden_states = self.norm(residual, hidden_states, next_params.attn_norm_gamma, beta=None) + else: + # On last layer, we just need to perform the residual add. Adding into the residual + # here is safe. + residual.add_(hidden_states) + + return residual, hidden_states + + def _forward_unembed(self, hidden_states: torch.Tensor, ragged_batch_info: RaggedBatchWrapper) -> torch.Tensor: + """ + Performs unembedding of the hidden states to logits. This will only sample the final + token of each sequence. + """ + logits = self.unembed(hidden_states, + self._non_transformer.word_unembed, + ragged_batch_info, + gamma=self._non_transformer.final_norm) + + if self.tp_size > 1: + comm_buffer = empty_from(self._comm_logits, (self.tp_size, logits.shape[0], logits.shape[1])) + full_logits = empty_from(self._return_logits, (logits.shape[0], self.vocab_size)) + + dist.all_gather_into_tensor(comm_buffer, logits, group=self._base_mp_group) + + full_logits.copy_(comm_buffer.permute(1, 0, 2).reshape(logits.shape[0], self.vocab_size)) + + return full_logits + else: + return logits + + def forward(self, wrapped_batch: RaggedBatchWrapper) -> torch.Tensor: + + residual = self._forward_embed(wrapped_batch) + + residual, hidden_states = self.norm(residual, None, self._transformer[0].attn_norm_gamma, beta=None) + + for layer_idx in range(self.num_layers): + residual, hidden_states = self._forward_transformer(layer_idx, residual, hidden_states, wrapped_batch) + + return self._forward_unembed(residual, wrapped_batch) diff --git a/deepspeed/inference/v2/model_implementations/qwen_v2_moe/policy.py b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/policy.py new file mode 100644 index 000000000000..630bafe993a8 --- /dev/null +++ b/deepspeed/inference/v2/model_implementations/qwen_v2_moe/policy.py @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +from typing import Any + +from ...config_v2 import RaggedInferenceEngineConfig +from ..inference_policy_base import ContainerMap, InferenceV2Policy +from .container import Qwen2MoeNonTransformerContainer, Qwen2MoeTransformerContainer +from .model import Qwen2MoeInferenceModel + + +class Qwen2MoePolicy(InferenceV2Policy): + + def instantiate_model(self, engine_config: RaggedInferenceEngineConfig, mp_group: Any) -> Qwen2MoeInferenceModel: + return Qwen2MoeInferenceModel(config=self._model_config, engine_config=engine_config, base_mp_group=mp_group) + + def build_container_map(self) -> ContainerMap: + map = ContainerMap() + + transformer_containers = [Qwen2MoeTransformerContainer(self.model) for _ in range(self.model.num_layers)] + + map.set_transformer_params(['model.layers'], transformer_containers) + + map.set_non_transformer_params(Qwen2MoeNonTransformerContainer(self.model)) + + map.set_unmapped_params([]) + + return map diff --git a/deepspeed/inference/v2/modules/implementations/moe/cutlass_multi_gemm.py b/deepspeed/inference/v2/modules/implementations/moe/cutlass_multi_gemm.py index 38c0000d7f78..bd90cbd5d697 100644 --- a/deepspeed/inference/v2/modules/implementations/moe/cutlass_multi_gemm.py +++ b/deepspeed/inference/v2/modules/implementations/moe/cutlass_multi_gemm.py @@ -42,7 +42,7 @@ def supports_config(config: DSMoEConfig) -> bool: if config.input_dtype != torch.float16 and config.input_dtype != torch.bfloat16: return False - if config.top_k != 1 and config.top_k != 2: + if config.top_k != 1 and config.top_k != 2 and config.top_k != 4: return False return True From 3c490f9cf45e6bf7841932cba3d8f3623a04c461 Mon Sep 17 00:00:00 2001 From: andyG <135115931+Andy666G@users.noreply.github.com> Date: Fri, 2 Aug 2024 04:28:55 +0800 Subject: [PATCH 11/14] Use accelerator to replace cuda in setup and runner (#5769) Use accelerator apis to select device in setup.py and set visible devices env in runner.py --------- Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- deepspeed/launcher/runner.py | 13 +++++++------ setup.py | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/deepspeed/launcher/runner.py b/deepspeed/launcher/runner.py index 12dd629b1b34..f140d73ee0d0 100755 --- a/deepspeed/launcher/runner.py +++ b/deepspeed/launcher/runner.py @@ -403,18 +403,19 @@ def main(args=None): resource_pool = fetch_hostfile(args.hostfile) - # respect CUDA_VISIBLE_DEVICES for a single node and no explicit resource filters - cuda_visible_devices = os.environ.get("CUDA_VISIBLE_DEVICES", "") - if not resource_pool and len(cuda_visible_devices): - detected_str = f"Detected CUDA_VISIBLE_DEVICES={cuda_visible_devices}" + # respect VISIBLE_DEVICES for a single node and no explicit resource filters + visible_devices_env = get_accelerator().visible_devices_envs()[0] + visible_devices = os.environ.get(visible_devices_env, "") + if not resource_pool and len(visible_devices): + detected_str = f"Detected VISIBLE_DEVICES={visible_devices}" if len(args.include) or len(args.exclude) or args.num_nodes > 1 or args.num_gpus > 0: print( f"{detected_str} but ignoring it because one or several of --include/--exclude/--num_gpus/--num_nodes cl args were used. If you want to use CUDA_VISIBLE_DEVICES don't pass any of these arguments to deepspeed." ) else: - args.include = f"localhost:{cuda_visible_devices}" + args.include = f"localhost:{visible_devices}" print(f"{detected_str}: setting --include={args.include}") - del os.environ["CUDA_VISIBLE_DEVICES"] + del os.environ[visible_devices_env] if args.num_nodes >= 0 or args.num_gpus >= 0: if args.include != "" or args.exclude != "": diff --git a/setup.py b/setup.py index 183d42907e82..2b7555361655 100755 --- a/setup.py +++ b/setup.py @@ -40,6 +40,8 @@ from op_builder.all_ops import ALL_OPS, accelerator_name from op_builder.builder import installed_cuda_version +from accelerator import get_accelerator + # Fetch rocm state. is_rocm_pytorch = OpBuilder.is_rocm_pytorch() rocm_version = OpBuilder.installed_rocm_version() @@ -91,7 +93,7 @@ def get_env_if_set(key, default: typing.Any = ""): } # Add specific cupy version to both onebit extension variants. -if torch_available and torch.cuda.is_available(): +if torch_available and get_accelerator().device_name() == 'cuda': cupy = None if is_rocm_pytorch: rocm_major, rocm_minor = rocm_version @@ -120,7 +122,6 @@ def get_env_if_set(key, default: typing.Any = ""): # For any pre-installed ops force disable ninja. if torch_available: - from accelerator import get_accelerator use_ninja = is_env_set("DS_ENABLE_NINJA") cmdclass['build_ext'] = get_accelerator().build_extension().with_options(use_ninja=use_ninja) @@ -131,7 +132,7 @@ def get_env_if_set(key, default: typing.Any = ""): TORCH_MAJOR = "0" TORCH_MINOR = "0" -if torch_available and not torch.cuda.is_available(): +if torch_available and not get_accelerator().device_name() == 'cuda': # Fix to allow docker builds, similar to https://github.com/NVIDIA/apex/issues/486. print("[WARNING] Torch did not find cuda available, if cross-compiling or running with cpu only " "you can ignore this message. Adding compute capability for Pascal, Volta, and Turing " From 029bb5274ab89c36de66ced121a28074d8f4acae Mon Sep 17 00:00:00 2001 From: Olatunji Ruwase Date: Thu, 1 Aug 2024 16:35:26 -0400 Subject: [PATCH 12/14] Link GDS blog to site (#5820) --- blogs/deepspeed-gds/README.md | 8 ++++++-- docs/index.md | 15 +++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/blogs/deepspeed-gds/README.md b/blogs/deepspeed-gds/README.md index a00b5083c3ea..6891f9f4667a 100644 --- a/blogs/deepspeed-gds/README.md +++ b/blogs/deepspeed-gds/README.md @@ -79,6 +79,10 @@ We measure the generation throughput of inferencing a LLAMA3-70B model on a sing Figure 3: Using DeepNVMe to scale LLAMA3-70B token generation performance with NVMe offloading. -# Conclusion +# Summary -In this blog post, we introduced DeepNVMe, an I/O optimization technology created to tackle the emergence of I/O operations as key bottlenecks of Deep Learning scalability. DeepNVMe enables fast and efficient data transfers between persistent storage and DL application memory through optimizations built on popular storage technologies such as NVMe SSDs and NVIDIA GDS. We showed benefits of using DeepNVMe for LLAMA3-70B token generation on single A100-80GB GPU with NVMe offloading, for which it achieves up to 7 tokens per second in generation throughput on an Azure NC96ads\_A100\_v4 VM. DeepNVMe will be generally available in DeepSpeed versions >= [0.15.0](https://github.com/microsoft/DeepSpeed/releases/tag/v0.15.0). In future blogs, we will report DeepNVMe improvements for other I/O bound DL applications such as model checkpointing and data loading. +In this blog post, we introduced DeepNVMe, an I/O optimization technology created to tackle the emergence of I/O operations as key bottlenecks of Deep Learning scalability. DeepNVMe enables fast and efficient data transfers between persistent storage and DL application memory through optimizations built on popular storage technologies such as NVMe SSDs and NVIDIA GDS. We showed benefits of using DeepNVMe for LLAMA3-70B token generation on single A100-80GB GPU with NVMe offloading, for which it achieves up to 7 tokens per second in generation throughput on an Azure NC96ads\_A100\_v4 VM. DeepNVMe will be open-sourced and generally available in DeepSpeed versions >= [0.15.0](https://github.com/microsoft/DeepSpeed/releases/tag/v0.15.0). In future blogs, we will report DeepNVMe improvements for other I/O bound DL applications such as model checkpointing and data loading. + + +# Acknowlegements +This work is the result of a deep collaboration between Microsoft and Nvidia. The contributors include Joe Mayer, Martin Cai, and Olatunji Ruwase from Microsoft; Kiran Modukuri, Vahid Noormofidi, Sourab Gupta, and Sandeep Joshi from Nivida. diff --git a/docs/index.md b/docs/index.md index 992c8ef6adb5..127c7226e6d4 100755 --- a/docs/index.md +++ b/docs/index.md @@ -7,26 +7,25 @@ title: "Latest News" --- DeepSpeed empowers ChatGPT-like model training with a single click, offering 15x speedup over SOTA RLHF systems with unprecedented cost reduction at all scales; [learn how](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-chat). +* [2024/08] [DeepNVMe: Improving DL Applications through I/O Optimizations](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed-gds/README.md)[[日本語](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed-gds/japanese/README.md)] [[中文](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed-gds/chinese/README.md)] * [2024/07] [DeepSpeed Universal Checkpointing: Efficient and Flexible Checkpointing for Large Scale Distributed Training](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-ucp/README.md)[[日本語](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-ucp/japanese/README.md)] * [2024/03] [DeepSpeed-FP6: The Power of FP6-Centric Serving for Large Language Models](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fp6/03-05-2024/README.md) [[English](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fp6/03-05-2024/README.md)] [[中文](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fp6/03-05-2024/README-Chinese.md)] * [2024/01] [DeepSpeed-FastGen: Introducting Mixtral, Phi-2, and Falcon support with major performance and feature enhancements.](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fastgen/2024-01-19) * [2023/11] [Llama 2 Inference on 4th Gen Intel® Xeon® Scalable Processor with DeepSpeed](https://github.com/microsoft/DeepSpeed/tree/master/blogs/intel-inference) [[Intel version]](https://www.intel.com/content/www/us/en/developer/articles/technical/xllama-2-on-xeon-scalable-processor-with-deepspeed.html) -* [2023/11] [DeepSpeed ZeRO-Offload++: 6x Higher Training Throughput via Collaborative CPU/GPU Twin-Flow](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-offloadpp) -* [2023/11] [DeepSpeed-FastGen: High-throughput Text Generation for LLMs via MII and DeepSpeed-Inference](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fastgen) [[English](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fastgen)] [[中文](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fastgen/chinese/README.md)] [[日本語](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-fastgen/japanese/README.md)] -* [2023/10] [DeepSpeed-VisualChat: Improve Your Chat Experience with Multi-Round Multi-Image Inputs](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-visualchat/10-03-2023/README.md) [[English](https://github.com/microsoft/DeepSpeed/tree/master/blogs/deepspeed-visualchat/10-03-2023/README.md)] [[中文](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed-visualchat/10-03-2023/README-Chinese.md)] [[日本語](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed-visualchat/10-03-2023/README-Japanese.md)] -* [2023/09] Announcing the DeepSpeed4Science Initiative: Enabling large-scale scientific discovery through sophisticated AI system technologies [[DeepSpeed4Science website](https://deepspeed4science.ai/)] [[Tutorials](https://www.deepspeed.ai/deepspeed4science/)] [[White paper](https://arxiv.org/abs/2310.04610)] [[Blog](https://www.microsoft.com/en-us/research/blog/announcing-the-deepspeed4science-initiative-enabling-large-scale-scientific-discovery-through-sophisticated-ai-system-technologies/)] [[中文](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed4science/chinese/README.md)] [[日本語](https://github.com/microsoft/DeepSpeed/blob/master/blogs/deepspeed4science/japanese/README.md)] -* [2023/08] [DeepSpeed ZeRO-Inference: 20x faster inference through weight quantization and KV cache offloading](https://github.com/microsoft/DeepSpeedExamples/blob/master/inference/huggingface/zero_inference/README.md)

More news
From 3a2d526ed6d7e59254fed4e96adfe796c42e6681 Mon Sep 17 00:00:00 2001 From: ic-synth Date: Fri, 2 Aug 2024 17:00:00 +0100 Subject: [PATCH 13/14] Non-reentrant checkpointing hook fix (#5781) This PR adds an extra condition to attach backward pass hooks to leaf nodes only if Synchronisation or Profiling is enabled, as otherwise these hooks are not necessary. Hook code below: ``` def after_backward_hook(_nonuse_grads): """the hook registered to all leaf tensors""" nonlocal leaf_tensors, backward_visited_leaf_nodes backward_visited_leaf_nodes += 1 if backward_visited_leaf_nodes == len(leaf_tensors): see_memory_usage("After backward checkpointing code after backward", force=False) if PROFILE_TIME: timers('backward').stop() timers.log(['backward']) if SYNCHRONIZE: get_accelerator().synchronize() ``` see_memory_usage is nevel used, as `force` is hardcoded to `False`. Thus this hook only does any real work only when PROFILE_TIME or SYNCHRONIZE is True. Otherwise it creates unnecessary function calls Co-authored-by: Heyang Qin Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- deepspeed/runtime/activation_checkpointing/checkpointing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deepspeed/runtime/activation_checkpointing/checkpointing.py b/deepspeed/runtime/activation_checkpointing/checkpointing.py index 529931ca0df1..f955cf5ebcad 100644 --- a/deepspeed/runtime/activation_checkpointing/checkpointing.py +++ b/deepspeed/runtime/activation_checkpointing/checkpointing.py @@ -965,8 +965,9 @@ def after_backward_hook(_nonuse_grads): with torch.autograd.graph.saved_tensors_hooks(checkpoint_pack, checkpoint_unpack): outputs = function(*inputs_cuda) - for leaf_tensor in leaf_tensors: - leaf_tensor.register_hook(after_backward_hook) + if PROFILE_TIME or SYNCHRONIZE: + for leaf_tensor in leaf_tensors: + leaf_tensor.register_hook(after_backward_hook) see_memory_usage("After running forward on the layer", force=False) From 2ef82232102663168abf942f7a8a3e9e049f0922 Mon Sep 17 00:00:00 2001 From: Olatunji Ruwase Date: Fri, 2 Aug 2024 13:18:01 -0400 Subject: [PATCH 14/14] Fix NV references (#5821) Fix NVIDIA references and typos. --------- Co-authored-by: Logan Adams <114770087+loadams@users.noreply.github.com> --- blogs/deepspeed-gds/README.md | 12 ++++++------ docs/Gemfile | 22 ---------------------- 2 files changed, 6 insertions(+), 28 deletions(-) delete mode 100644 docs/Gemfile diff --git a/blogs/deepspeed-gds/README.md b/blogs/deepspeed-gds/README.md index 6891f9f4667a..34416c07ea4d 100644 --- a/blogs/deepspeed-gds/README.md +++ b/blogs/deepspeed-gds/README.md @@ -17,17 +17,17 @@ this problem, DeepSpeed has created a suite of I/O optimizations collectively ca DeepNVMe improves the performance and efficiency of I/O-bound DL applications by accelerating I/O operations and reducing hardware requirements. It achieves this by leveraging storage innovations such as Non-Volatile -Memory Express (NVMe) Solid Storage Devices (SSDs) and Nvidia Magnum IO^TM GPUDirect® Storage (GDS). In this +Memory Express (NVMe) Solid Storage Devices (SSDs) and NVIDIA Magnum IOTM GPUDirect® Storage (GDS). In this blog we show the benefits of DeepNVMe using microbenchmarks and an inference application. In experiments conducted on an Azure NC96ads\_A100\_v4 VM, we observed that DeepNVMe saturates available NVMe bandwidth for data transfers with GPU or CPU memory, achieving up to 10GB/sec reads and 5 GB/secs writes. # Background -High-performance access to persistent storage is a common challenge in many computing domains, including DL. Thus, a significant number of hardware and software solutions have been proposed. DeepNVMe builds on three such solutions: (1) NVMe SSDs, (2) Nvidia GDS, and (3) Linux Asynchronous I/O (libaio). We will briefly describe each of these technologies. +High-performance access to persistent storage is a common challenge in many computing domains, including DL. Thus, a significant number of hardware and software solutions have been proposed. DeepNVMe builds on three such solutions: (1) NVMe SSDs, (2) NVIDIA GDS, and (3) Linux Asynchronous I/O (libaio). We will briefly describe each of these technologies. -NVMe SSDs are Flash-based storage devices that are replacing much slower hard disk drives (HDD) as primary persistent storage in modern servers. For example, an Azure NC96ads\_A100\_v4 VM is equipped with four NVMe SSDs which are individually capable of 3.25 GB/sec reads and can be combined in a RAID-0 configuration for a theoretical aggregate read bandwidth of 13 GB/sec. Nvidia GDS enables direct transfers between NVMe and GPU memory thus avoiding the inefficiencies of the traditional approach of using intermediate CPU memory (bounce buffer). Nvidia GDS is generally available in CUDA versions 11.4 and above. Finally, libaio is an asynchronous I/O stack introduced in Linux to better extract raw performance of fast storage devices like NVMe SSDs compared to the traditional I/O stack. +NVMe SSDs are Flash-based storage devices that are replacing much slower hard disk drives (HDD) as primary persistent storage in modern servers. For example, an Azure NC96ads\_A100\_v4 VM is equipped with four NVMe SSDs which are individually capable of 3.25 GB/sec reads and can be combined in a RAID-0 configuration for a theoretical aggregate read bandwidth of 13 GB/sec. NVIDIA GDS enables direct transfers between NVMe and GPU memory thus avoiding the inefficiencies of the traditional approach of using intermediate CPU memory (bounce buffer). NVIDIA GDS is generally available in CUDA versions 11.4 and above. Finally, libaio is an asynchronous I/O stack introduced in Linux to better extract raw performance of fast storage devices like NVMe SSDs compared to the traditional I/O stack. -# DeepNVMe: an Optimization Module for DeepLearning I/O +# DeepNVMe: an Optimization Module for Deep Learning I/O DeepNVMe is a Python module that we developed with two key design principles. First, it leverages the above discussed storage technologies to implement powerful optimizations such as non-blocking I/O operations, bulk submission of I/O operations, parallelization of an individual I/O operation, and a lightweight runtime. Second, it exposes these I/O optimizations through a simple POSIX-like interface to foster easy integration into DL applications while avoiding the complexities of the underlying technologies. @@ -43,7 +43,7 @@ Table 1: Experimental setup details ## Microbenchmark Performance -We used three benchmarking tools for our evaluations. The first is fio, the popular I/O benchmarking tool written in C. The second is gdsio from Nvidia for benchmarking GDS performance. The third is ds\_io, a Python tool that we created for easy integration with DeepNVMe and to be more representative of DL applications which are commonly Python-based. +We used three benchmarking tools for our evaluations. The first is fio, the popular I/O benchmarking tool written in C. The second is gdsio from NVIDIA for benchmarking GDS performance. The third is ds\_io, a Python tool that we created for easy integration with DeepNVMe and to be more representative of DL applications which are commonly Python-based. ## High-Performance I/O with CPU Buffers via NVMe Scaling @@ -85,4 +85,4 @@ In this blog post, we introduced DeepNVMe, an I/O optimization technology create # Acknowlegements -This work is the result of a deep collaboration between Microsoft and Nvidia. The contributors include Joe Mayer, Martin Cai, and Olatunji Ruwase from Microsoft; Kiran Modukuri, Vahid Noormofidi, Sourab Gupta, and Sandeep Joshi from Nivida. +This work is the result of a deep collaboration between Microsoft and NVIDIA. The contributors include Joe Mayer, Martin Cai, and Olatunji Ruwase from Microsoft; Kiran Modukuri, Vahid Noormofidi, Sourab Gupta, and Sandeep Joshi from Nivida. diff --git a/docs/Gemfile b/docs/Gemfile deleted file mode 100644 index 888e3c8dfd6a..000000000000 --- a/docs/Gemfile +++ /dev/null @@ -1,22 +0,0 @@ -source "https://rubygems.org" - -gem 'github-pages', group: :jekyll_plugins - -# If you have any plugins, put them here! -group :jekyll_plugins do - gem "jekyll-feed" - gem "jekyll-paginate" - gem "jekyll-remote-theme" - gem "jekyll-include-cache" - gem "minimal-mistakes-jekyll" -end - -# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem -# and associated library. -install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do - gem "tzinfo", "~> 1.2" - gem "tzinfo-data" -end - -# Performance-booster for watching directories on Windows -gem "wdm", "~> 0.1.1", :install_if => Gem.win_platform?